From 3f827ef22bfa76439fcedc4c191204ac69f8d1f4 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Wed, 15 Sep 2021 09:20:42 +0530 Subject: [PATCH] UI: Support to upload resource icons (#5157) * Resource Icon support - backend * Add API support for resourceicon * update reponse params + ui support * Add exclusive list api for icons and UI changes * refactor upload view * UI changes to support resource icon wherever necessary * convert api to POST + refactor icon view * Add response name to list API + cosmetic changes in UI * Added support for the following: resource icon support for vpcs, networks, domains, and projects add icons to list view if reosurces support icons to be added support for showing project icons in the project switching drop-down menu * List resourceicon cmds to be allowed for user role too Users to inherit account icon if present (in listUsers response) Move common code to plugin.js Add icon to project list view - while switching between projects - Dashboard page Show icons against zones - Capacity Dashboard view Show user / account icon at the login button if present * cosmetic changes * optimize ui code * fix reload issue for domain view * add access check for delete operation * ui-related changes to show iso icons * iso image in uservm response * add icons to custom form's list resources * some more custom forms aligned to show icon for resources * conmitic changes + add listing of icons to listdomainchildren cmd * Add backend/server-side validation for base64 string passed for image * change preview border * preselect zone if there's only one * add default icon * show icon for network list in deploy vm view * add custom icons if any to the import-export VM view * preselect zone persistence on clearing cache * prevent root vol from inheriting template/iso icon * show tempalte icon in the info card details * fix icon not being show on hard-refresh / initial traversal * fx success message --- .../main/java/com/cloud/event/EventTypes.java | 4 + .../java/com/cloud/server/ResourceIcon.java | 32 ++ .../com/cloud/server/ResourceIconManager.java | 28 ++ .../com/cloud/server/ResourceManagerUtil.java | 24 ++ .../java/com/cloud/server/ResourceTag.java | 31 +- .../cloud/server/TaggedResourceService.java | 12 - .../apache/cloudstack/api/ApiConstants.java | 3 + .../org/apache/cloudstack/api/BaseCmd.java | 12 +- .../cloudstack/api/ResponseGenerator.java | 6 +- .../admin/domain/ListDomainChildrenCmd.java | 25 ++ .../command/admin/domain/ListDomainsCmd.java | 25 ++ .../resource/icon/DeleteResourceIconCmd.java | 104 ++++++ .../resource/icon/ListResourceIconCmd.java | 86 +++++ .../resource/icon/UploadResourceIconCmd.java | 144 ++++++++ .../api/command/admin/user/ListUsersCmd.java | 30 ++ .../api/command/admin/zone/CreateZoneCmd.java | 2 +- .../api/command/admin/zone/UpdateZoneCmd.java | 2 +- .../command/user/account/ListAccountsCmd.java | 25 ++ .../api/command/user/iso/ListIsosCmd.java | 26 ++ .../command/user/network/ListNetworksCmd.java | 29 ++ .../command/user/project/ListProjectsCmd.java | 25 ++ .../user/resource/ListDetailOptionsCmd.java | 4 +- .../api/command/user/tag/CreateTagsCmd.java | 8 +- .../api/command/user/tag/DeleteTagsCmd.java | 2 +- .../user/template/ListTemplatesCmd.java | 24 ++ .../api/command/user/vm/ListVMsCmd.java | 35 ++ .../user/volume/AddResourceDetailCmd.java | 2 +- .../user/volume/ListResourceDetailsCmd.java | 2 +- .../user/volume/RemoveResourceDetailCmd.java | 2 +- .../api/command/user/vpc/ListVPCsCmd.java | 25 ++ .../api/command/user/vpn/AddVpnUserCmd.java | 2 +- .../api/command/user/zone/ListZonesCmd.java | 8 +- .../api/response/AccountResponse.java | 10 +- .../api/response/DomainResponse.java | 11 +- .../api/response/NetworkResponse.java | 19 +- .../api/response/ProjectResponse.java | 15 +- .../api/response/ResourceIconResponse.java | 61 ++++ .../api/response/SetResourceIconResponse.java | 21 ++ .../api/response/TemplateResponse.java | 11 +- .../cloudstack/api/response/UserResponse.java | 11 +- .../api/response/UserVmResponse.java | 15 +- .../cloudstack/api/response/VpcResponse.java | 15 +- .../cloudstack/api/response/ZoneResponse.java | 15 +- .../apache/cloudstack/query/QueryService.java | 4 + .../cloud/resource/icon/ResourceIconVO.java | 167 ++++++++++ .../resource/icon/dao/ResourceIconDao.java | 31 ++ .../icon/dao/ResourceIconDaoImpl.java | 79 +++++ ...spring-engine-schema-core-daos-context.xml | 1 + .../META-INF/db/schema-41520to41600.sql | 14 + .../cloudstack/api/ListVMsMetricsCmd.java | 1 + .../main/java/com/cloud/api/ApiDBUtils.java | 27 +- .../java/com/cloud/api/ApiResponseHelper.java | 11 +- .../com/cloud/api/query/QueryManagerImpl.java | 27 +- .../cloud/api/query/ViewResponseHelper.java | 4 +- .../api/query/dao/DataCenterJoinDao.java | 2 +- .../api/query/dao/DataCenterJoinDaoImpl.java | 12 +- .../metadata/ResourceMetaDataManagerImpl.java | 7 +- .../resourceicon/ResourceIconManagerImpl.java | 230 +++++++++++++ .../cloud/server/ManagementServerImpl.java | 6 + .../cloud/tags/ResourceManagerUtilImpl.java | 186 +++++++++++ .../cloud/tags/TaggedResourceManagerImpl.java | 152 +-------- .../spring-server-core-managers-context.xml | 6 + .../metadata/ResourceMetaDataManagerTest.java | 7 +- tools/apidoc/gen_toc.py | 1 + ui/public/locales/en.json | 7 + ui/src/components/header/ProjectMenu.vue | 8 +- ui/src/components/header/UserMenu.vue | 46 ++- ui/src/components/view/InfoCard.vue | 194 ++++++++++- ui/src/components/view/ListView.vue | 27 +- ui/src/components/view/ResourceIcon.vue | 59 ++++ ui/src/components/view/SearchView.vue | 24 +- ui/src/components/view/TreeView.vue | 10 +- ui/src/components/view/UploadResourceIcon.vue | 314 ++++++++++++++++++ ui/src/core/lazy_lib/components_use.js | 2 + ui/src/main.js | 4 +- ui/src/utils/plugins.js | 33 ++ ui/src/views/AutogenView.vue | 60 +++- ui/src/views/compute/AssignInstance.vue | 16 + .../views/compute/CreateKubernetesCluster.vue | 7 +- ui/src/views/compute/DeployVM.vue | 65 +++- ui/src/views/compute/InstanceTab.vue | 5 + .../views/compute/wizard/NetworkSelection.vue | 14 +- .../compute/wizard/TemplateIsoRadioGroup.vue | 13 +- ui/src/views/dashboard/CapacityDashboard.vue | 8 +- ui/src/views/iam/AddAccount.vue | 7 +- ui/src/views/iam/AddUser.vue | 11 +- ui/src/views/iam/DomainView.vue | 11 +- .../image/AddKubernetesSupportedVersion.vue | 5 + ui/src/views/image/IsoZones.vue | 29 +- ui/src/views/image/RegisterOrUploadIso.vue | 9 + .../views/image/RegisterOrUploadTemplate.vue | 10 +- ui/src/views/image/TemplateZones.vue | 29 +- .../image/UpdateTemplateIsoPermissions.vue | 12 +- ui/src/views/infra/AddPrimaryStorage.vue | 8 +- ui/src/views/infra/AddSecondaryStorage.vue | 13 +- ui/src/views/infra/ClusterAdd.vue | 8 +- ui/src/views/infra/HostAdd.vue | 8 +- ui/src/views/infra/PodAdd.vue | 8 +- .../views/infra/network/DedicatedVLANTab.vue | 17 +- .../views/infra/network/IpRangesTabGuest.vue | 7 +- .../network/CreateIsolatedNetworkForm.vue | 7 +- ui/src/views/network/CreateL2NetworkForm.vue | 10 +- .../views/network/CreateSharedNetworkForm.vue | 13 +- ui/src/views/network/CreateVpc.vue | 6 +- ui/src/views/network/NicsTable.vue | 56 +++- ui/src/views/offering/AddComputeOffering.vue | 8 + ui/src/views/offering/AddDiskOffering.vue | 8 + ui/src/views/offering/AddNetworkOffering.vue | 8 + ui/src/views/offering/AddVpcOffering.vue | 8 + .../views/offering/ImportBackupOffering.vue | 8 +- .../views/offering/UpdateOfferingAccess.vue | 10 + ui/src/views/storage/CreateVolume.vue | 6 +- ui/src/views/storage/UploadLocalVolume.vue | 6 +- .../views/tools/ImportUnmanagedInstance.vue | 53 ++- ui/src/views/tools/ManageInstances.vue | 21 +- 115 files changed, 3001 insertions(+), 303 deletions(-) create mode 100644 api/src/main/java/com/cloud/server/ResourceIcon.java create mode 100644 api/src/main/java/com/cloud/server/ResourceIconManager.java create mode 100644 api/src/main/java/com/cloud/server/ResourceManagerUtil.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/DeleteResourceIconCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/ListResourceIconCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/UploadResourceIconCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/ResourceIconResponse.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/SetResourceIconResponse.java create mode 100644 engine/schema/src/main/java/com/cloud/resource/icon/ResourceIconVO.java create mode 100644 engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java create mode 100644 engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java create mode 100644 server/src/main/java/com/cloud/resourceicon/ResourceIconManagerImpl.java create mode 100644 server/src/main/java/com/cloud/tags/ResourceManagerUtilImpl.java create mode 100644 ui/src/components/view/ResourceIcon.vue create mode 100644 ui/src/components/view/UploadResourceIcon.vue diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 5947334016c..72e623f139a 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -493,6 +493,10 @@ public class EventTypes { public static final String EVENT_TAGS_CREATE = "CREATE_TAGS"; public static final String EVENT_TAGS_DELETE = "DELETE_TAGS"; + // resource icon related events + public static final String EVENT_RESOURCE_ICON_UPLOAD = "UPLOAD.RESOURCE.ICON"; + public static final String EVENT_RESOURCE_ICON_DELETE = "DELETE.RESOURCE.ICON"; + // meta data related events public static final String EVENT_RESOURCE_DETAILS_CREATE = "CREATE_RESOURCE_DETAILS"; public static final String EVENT_RESOURCE_DETAILS_DELETE = "DELETE_RESOURCE_DETAILS"; diff --git a/api/src/main/java/com/cloud/server/ResourceIcon.java b/api/src/main/java/com/cloud/server/ResourceIcon.java new file mode 100644 index 00000000000..a5ac3ee6519 --- /dev/null +++ b/api/src/main/java/com/cloud/server/ResourceIcon.java @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.server; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface ResourceIcon extends Identity, InternalIdentity { + long getResourceId(); + + void setResourceId(long resourceId); + + ResourceTag.ResourceObjectType getResourceType(); + + String getResourceUuid(); + + String getIcon(); +} diff --git a/api/src/main/java/com/cloud/server/ResourceIconManager.java b/api/src/main/java/com/cloud/server/ResourceIconManager.java new file mode 100644 index 00000000000..e5111d9160b --- /dev/null +++ b/api/src/main/java/com/cloud/server/ResourceIconManager.java @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.server; + +import java.util.List; + +public interface ResourceIconManager { + + boolean uploadResourceIcon(List resourceIds, ResourceTag.ResourceObjectType resourceType, String base64Image); + + boolean deleteResourceIcon(List resourceIds, ResourceTag.ResourceObjectType resourceType); + + ResourceIcon getByResourceTypeAndUuid(ResourceTag.ResourceObjectType type, String resourceId); +} diff --git a/api/src/main/java/com/cloud/server/ResourceManagerUtil.java b/api/src/main/java/com/cloud/server/ResourceManagerUtil.java new file mode 100644 index 00000000000..9a3b51a70d5 --- /dev/null +++ b/api/src/main/java/com/cloud/server/ResourceManagerUtil.java @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.server; + +public interface ResourceManagerUtil { + long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType); + String getUuid(String resourceId, ResourceTag.ResourceObjectType resourceType); + ResourceTag.ResourceObjectType getResourceType(String resourceTypeStr); + void checkResourceAccessible(Long accountId, Long domainId, String exceptionMessage); +} diff --git a/api/src/main/java/com/cloud/server/ResourceTag.java b/api/src/main/java/com/cloud/server/ResourceTag.java index fb07762639a..ba5ab8ad0ff 100644 --- a/api/src/main/java/com/cloud/server/ResourceTag.java +++ b/api/src/main/java/com/cloud/server/ResourceTag.java @@ -24,13 +24,13 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit // FIXME - extract enum to another interface as its used both by resourceTags and resourceMetaData code public enum ResourceObjectType { - UserVm(true, true), - Template(true, true), - ISO(true, false), + UserVm(true, true, true), + Template(true, true, true), + ISO(true, false, true), Volume(true, true), Snapshot(true, false), Backup(true, false), - Network(true, true), + Network(true, true, true), Nic(false, true), LoadBalancer(true, true), PortForwardingRule(true, true), @@ -38,14 +38,14 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit SecurityGroup(true, false), SecurityGroupRule(true, false), PublicIpAddress(true, true), - Project(true, false), - Account(true, false), - Vpc(true, true), + Project(true, false, true), + Account(true, false, true), + Vpc(true, true, true), NetworkACL(true, true), StaticRoute(true, false), VMSnapshot(true, false), RemoteAccessVpn(true, true), - Zone(false, true), + Zone(false, true, true), ServiceOffering(false, true), Storage(false, true), PrivateGateway(false, true), @@ -53,7 +53,7 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit VpnGateway(false, true), CustomerGateway(false, true), VpnConnection(false, true), - User(true, true), + User(true, true, true), DiskOffering(false, true), AutoScaleVmProfile(false, true), AutoScaleVmGroup(false, true), @@ -62,7 +62,8 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit SnapshotPolicy(true, true), GuestOs(false, true), NetworkOffering(false, true), - VpcOffering(true, false); + VpcOffering(true, false), + Domain(false, false, true); ResourceObjectType(boolean resourceTagsSupport, boolean resourceMetadataSupport) { @@ -70,8 +71,14 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit metadataSupport = resourceMetadataSupport; } + ResourceObjectType(boolean resourceTagsSupport, boolean resourceMetadataSupport, boolean resourceIconSupport) { + this(resourceTagsSupport, resourceMetadataSupport); + this.resourceIconSupport = resourceIconSupport; + } + private final boolean resourceTagsSupport; private final boolean metadataSupport; + private boolean resourceIconSupport; public boolean resourceTagsSupport() { return resourceTagsSupport; @@ -80,6 +87,10 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit public boolean resourceMetadataSupport() { return metadataSupport; } + + public boolean resourceIconSupport() { + return resourceIconSupport; + } } /** diff --git a/api/src/main/java/com/cloud/server/TaggedResourceService.java b/api/src/main/java/com/cloud/server/TaggedResourceService.java index 84f7eb0a6f0..27fa21978aa 100644 --- a/api/src/main/java/com/cloud/server/TaggedResourceService.java +++ b/api/src/main/java/com/cloud/server/TaggedResourceService.java @@ -43,18 +43,6 @@ public interface TaggedResourceService { List listByResourceTypeAndId(ResourceObjectType type, long resourceId); - //FIXME - the methods below should be extracted to its separate manager/service responsible just for retrieving object details - ResourceObjectType getResourceType(String resourceTypeStr); - - /** - * @param resourceId - * @param resourceType - * @return - */ - String getUuid(String resourceId, ResourceObjectType resourceType); - - public long getResourceId(String resourceId, ResourceObjectType resourceType); - /** * Retrieves tags from resource. * @param type diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index f930800b9f8..35c1f87745b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -37,6 +37,7 @@ public class ApiConstants { public static final String BACKUP_ID = "backupid"; public static final String BACKUP_OFFERING_NAME = "backupofferingname"; public static final String BACKUP_OFFERING_ID = "backupofferingid"; + public static final String BASE64_IMAGE = "base64image"; public static final String BITS = "bits"; public static final String BOOTABLE = "bootable"; public static final String BIND_DN = "binddn"; @@ -333,6 +334,7 @@ public class ApiConstants { public static final String SESSIONKEY = "sessionkey"; public static final String SHOW_CAPACITIES = "showcapacities"; public static final String SHOW_REMOVED = "showremoved"; + public static final String SHOW_RESOURCE_ICON = "showicon"; public static final String SHOW_UNIQUE = "showunique"; public static final String SIGNATURE = "signature"; public static final String SIGNATURE_VERSION = "signatureversion"; @@ -747,6 +749,7 @@ public class ApiConstants { public static final String ACCESS_TYPE = "accesstype"; public static final String RESOURCE_DETAILS = "resourcedetails"; + public static final String RESOURCE_ICON = "icon"; public static final String EXPUNGE = "expunge"; public static final String FOR_DISPLAY = "fordisplay"; public static final String PASSIVE = "passive"; diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java index c897aad4d4b..6575f1e6062 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java @@ -29,6 +29,11 @@ import java.util.regex.Pattern; import javax.inject.Inject; +import com.cloud.server.ManagementService; +import com.cloud.server.ResourceIconManager; +import com.cloud.server.ResourceManagerUtil; +import com.cloud.server.ResourceMetaDataService; +import com.cloud.server.TaggedResourceService; import org.apache.cloudstack.acl.ProjectRoleService; import org.apache.cloudstack.acl.RoleService; import org.apache.cloudstack.acl.RoleType; @@ -67,9 +72,6 @@ import com.cloud.network.vpn.RemoteAccessVpnService; import com.cloud.network.vpn.Site2SiteVpnService; import com.cloud.projects.ProjectService; import com.cloud.resource.ResourceService; -import com.cloud.server.ManagementService; -import com.cloud.server.ResourceMetaDataService; -import com.cloud.server.TaggedResourceService; import com.cloud.storage.DataStoreProviderApiService; import com.cloud.storage.StorageService; import com.cloud.storage.VolumeApiService; @@ -164,6 +166,8 @@ public abstract class BaseCmd { @Inject public TaggedResourceService _taggedResourceService; @Inject + public ResourceManagerUtil resourceManagerUtil; + @Inject public ResourceMetaDataService _resourceMetaDataService; @Inject public VpcService _vpcService; @@ -201,6 +205,8 @@ public abstract class BaseCmd { public UUIDManager _uuidMgr; @Inject public AnnotationService annotationService; + @Inject + public ResourceIconManager resourceIconManager; public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException; diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 3f0d978b161..03f0a3c8369 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import com.cloud.server.ResourceIcon; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import com.cloud.resource.RollingMaintenanceManager; import org.apache.cloudstack.api.response.RollingMaintenanceResponse; @@ -270,7 +272,7 @@ public interface ResponseGenerator { PodResponse createPodResponse(Pod pod, Boolean showCapacities); - ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities); + ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities, Boolean showResourceIcon); VolumeResponse createVolumeResponse(ResponseView view, Volume volume); @@ -487,4 +489,6 @@ public interface ResponseGenerator { RollingMaintenanceResponse createRollingMaintenanceResponse(Boolean success, String details, List hostsUpdated, List hostsSkipped); + ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon); + } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainChildrenCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainChildrenCmd.java index cf35295358b..fb6c5be56ac 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainChildrenCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainChildrenCmd.java @@ -19,6 +19,9 @@ package org.apache.cloudstack.api.command.admin.domain; import java.util.ArrayList; import java.util.List; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -58,6 +61,10 @@ public class ListDomainChildrenCmd extends BaseListCmd { description = "If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false") private Boolean listAll; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, + description = "flag to display the resource icon for domains") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -78,6 +85,10 @@ public class ListDomainChildrenCmd extends BaseListCmd { return recursive == null ? false : recursive; } + public Boolean getShowIcon() { + return showIcon != null ? showIcon : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -100,6 +111,20 @@ public class ListDomainChildrenCmd extends BaseListCmd { response.setResponses(domainResponses, result.second()); response.setResponseName(getCommandName()); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateDomainResponse(response.getResponses()); + } this.setResponseObject(response); } + + private void updateDomainResponse(List response) { + for (DomainResponse domainResponse : response) { + ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Domain, domainResponse.getId()); + if (resourceIcon == null) { + continue; + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + domainResponse.setResourceIconResponse(iconResponse); + } + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmd.java index 5e4cda33a42..8b6661f27ff 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmd.java @@ -20,6 +20,9 @@ import java.util.ArrayList; import java.util.EnumSet; import java.util.List; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -66,6 +69,10 @@ public class ListDomainsCmd extends BaseListCmd implements UserCmd { description = "comma separated list of domain details requested, value can be a list of [ all, resource, min]") private List viewDetails; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, + description = "flag to display the resource icon for domains") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -105,6 +112,10 @@ public class ListDomainsCmd extends BaseListCmd implements UserCmd { return dv; } + public Boolean getShowIcon() { + return showIcon != null ? showIcon : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -119,5 +130,19 @@ public class ListDomainsCmd extends BaseListCmd implements UserCmd { ListResponse response = _queryService.searchForDomains(this); response.setResponseName(getCommandName()); this.setResponseObject(response); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateDomainResponse(response.getResponses()); + } + } + + private void updateDomainResponse(List response) { + for (DomainResponse domainResponse : response) { + ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Domain, domainResponse.getId()); + if (resourceIcon == null) { + continue; + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + domainResponse.setResourceIconResponse(iconResponse); + } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/DeleteResourceIconCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/DeleteResourceIconCmd.java new file mode 100644 index 00000000000..de45839bb09 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/DeleteResourceIconCmd.java @@ -0,0 +1,104 @@ +// 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.resource.icon; + +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import java.util.List; + +@APICommand(name = "deleteResourceIcon", description = "deletes the resource icon from the specified resource(s)", + responseObject = SuccessResponse.class, since = "4.16.0.0", entityType = {ResourceIcon.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User}) +public class DeleteResourceIconCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteResourceIconCmd.class.getName()); + + private static final String s_name = "deleteresourceiconresponse"; + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.RESOURCE_IDS, + type = BaseCmd.CommandType.LIST, + required = true, + collectionType = BaseCmd.CommandType.STRING, + description = "list of resources to upload the icon/image for") + private List resourceIds; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, required = true, description = "type of the resource") + private String resourceType; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public List getResourceIds() { + return resourceIds; + } + + public ResourceTag.ResourceObjectType getResourceType() { + return resourceManagerUtil.getResourceType(resourceType); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + try { + boolean result = resourceIconManager.deleteResourceIcon(getResourceIds(), getResourceType()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete resource image"); + } + + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getLocalizedMessage()); + } + + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount();// Let's give the caller here for event logging. + if (account != null) { + return account.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/ListResourceIconCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/ListResourceIconCmd.java new file mode 100644 index 00000000000..bc8eb009072 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/ListResourceIconCmd.java @@ -0,0 +1,86 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.resource.icon; + +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.log4j.Logger; + +import java.util.List; + +@APICommand(name = "listResourceIcon", description = "Lists the resource icon for the specified resource(s)", + responseObject = ResourceIconResponse.class, since = "4.16.0.0", entityType = {ResourceIcon.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User}) +public class ListResourceIconCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ListResourceIconCmd.class.getName()); + + private static final String s_name = "listresourceiconresponse"; + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.RESOURCE_IDS, + type = BaseCmd.CommandType.LIST, + required = true, + collectionType = BaseCmd.CommandType.STRING, + description = "list of resources to upload the icon/image for") + private List resourceIds; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, required = true, description = "type of the resource") + private String resourceType; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public List getResourceIds() { + return resourceIds; + } + + public ResourceTag.ResourceObjectType getResourceType() { + return resourceManagerUtil.getResourceType(resourceType); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + ListResponse response = _queryService.listResourceIcons(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/UploadResourceIconCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/UploadResourceIconCmd.java new file mode 100644 index 00000000000..3075bf3a1ab --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/icon/UploadResourceIconCmd.java @@ -0,0 +1,144 @@ +// 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.resource.icon; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; + +import java.awt.image.BufferedImage; + +import javax.imageio.ImageIO; +import java.io.ByteArrayInputStream; +import java.util.Base64; +import java.util.List; + + +@APICommand(name = "uploadResourceIcon", description = "Uploads an icon for the specified resource(s)", + responseObject = SuccessResponse.class, since = "4.16.0.0", entityType = {ResourceIcon.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User}) +public class UploadResourceIconCmd extends BaseCmd { + public static final Logger LOGGER = Logger.getLogger(UploadResourceIconCmd.class.getName()); + + private static final String s_name = "uploadresourceiconresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.RESOURCE_IDS, + type = BaseCmd.CommandType.LIST, + required = true, + collectionType = BaseCmd.CommandType.STRING, + description = "list of resources to upload the icon/image for") + private List resourceIds; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, required = true, description = "type of the resource") + private String resourceType; + + @Parameter(name = ApiConstants.BASE64_IMAGE, type = BaseCmd.CommandType.STRING, required = true, + description = "Base64 string representation of the resource icon/image", length = 2097152) + private String image; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public List getResourceIds() { + return resourceIds; + } + + public ResourceTag.ResourceObjectType getResourceType() { + return resourceManagerUtil.getResourceType(resourceType); + } + + public String getImage() { + if (StringUtils.isEmpty(image)) { + throw new InvalidParameterValueException("No image provided for resource icon"); + } + return image; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + try { + if (!imageValidator(getImage())) { + throw new InvalidParameterValueException("Invalid image uploaded"); + } + boolean result = resourceIconManager.uploadResourceIcon(getResourceIds(), getResourceType(), getImage()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to upload resource image"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getLocalizedMessage()); + } + } + + private boolean imageValidator (String base64Image) { + BufferedImage image = null; + byte[] imageByte; + try { + imageByte = Base64.getDecoder().decode(base64Image); + ByteArrayInputStream bis = new ByteArrayInputStream(imageByte); + image = ImageIO.read(bis); + bis.close(); + if (image == null) { + return false; + } + } catch (Exception e) { + LOGGER.warn("Data uploaded not a valid image"); + return false; + } + return true; + } + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount();// Let's give the caller here for event logging. + if (account != null) { + return account.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java index c0c2b24814a..a1adee7909f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java @@ -16,6 +16,9 @@ // under the License. package org.apache.cloudstack.api.command.admin.user; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -25,6 +28,8 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UserResponse; +import java.util.List; + @APICommand(name = "listUsers", description = "Lists user accounts", responseObject = UserResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ListUsersCmd extends BaseListAccountResourcesCmd { @@ -50,6 +55,10 @@ public class ListUsersCmd extends BaseListAccountResourcesCmd { @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "List user by the username") private String username; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, + description = "flag to display the resource icon for users") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -70,6 +79,10 @@ public class ListUsersCmd extends BaseListAccountResourcesCmd { return username; } + public Boolean getShowIcon() { + return showIcon != null ? showIcon : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -84,5 +97,22 @@ public class ListUsersCmd extends BaseListAccountResourcesCmd { ListResponse response = _queryService.searchForUsers(this); response.setResponseName(getCommandName()); this.setResponseObject(response); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateUserResponse(response.getResponses()); + } + } + + private void updateUserResponse(List response) { + for (UserResponse userResponse : response) { + ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.User, userResponse.getObjectId()); + if (resourceIcon == null) { + resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Account, userResponse.getAccountId()); + if (resourceIcon == null) { + continue; + } + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + userResponse.setResourceIconResponse(iconResponse); + } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java index 414c058e66d..af377cd347a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java @@ -171,7 +171,7 @@ public class CreateZoneCmd extends BaseCmd { CallContext.current().setEventDetails("Zone Name: " + getZoneName()); DataCenter result = _configService.createZone(this); if (result != null){ - ZoneResponse response = _responseGenerator.createZoneResponse(ResponseView.Full, result, false); + ZoneResponse response = _responseGenerator.createZoneResponse(ResponseView.Full, result, false, false); response.setResponseName(getCommandName()); setResponseObject(response); } else { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateZoneCmd.java index 2f5feed803d..ac80ca59f92 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateZoneCmd.java @@ -189,7 +189,7 @@ public class UpdateZoneCmd extends BaseCmd { CallContext.current().setEventDetails("Zone Id: " + getId()); DataCenter result = _configService.editZone(this); if (result != null) { - ZoneResponse response = _responseGenerator.createZoneResponse(ResponseView.Full, result, false); + ZoneResponse response = _responseGenerator.createZoneResponse(ResponseView.Full, result, false, false); response.setResponseName(getCommandName()); setResponseObject(response); } else { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmd.java index 29f86c84a12..993384d6a3f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmd.java @@ -20,6 +20,9 @@ import java.util.ArrayList; import java.util.EnumSet; import java.util.List; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -68,6 +71,10 @@ public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserC description = "comma separated list of account details requested, value can be a list of [ all, resource, min]") private List viewDetails; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, + description = "flag to display the resource icon for accounts") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -111,6 +118,10 @@ public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserC return dv; } + public Boolean getShowIcon() { + return showIcon != null ? showIcon : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -125,5 +136,19 @@ public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserC ListResponse response = _queryService.searchForAccounts(this); response.setResponseName(getCommandName()); setResponseObject(response); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateAccountResponse(response.getResponses()); + } + } + + private void updateAccountResponse(List response) { + for (AccountResponse accountResponse : response) { + ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Account, accountResponse.getObjectId()); + if (resourceIcon == null) { + continue; + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + accountResponse.setResourceIconResponse(iconResponse); + } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java index b741ae7dd5f..44bbb7168c1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java @@ -16,6 +16,9 @@ // under the License. package org.apache.cloudstack.api.command.user.iso; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -33,6 +36,8 @@ import org.apache.cloudstack.context.CallContext; import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; +import java.util.List; + @APICommand(name = "listIsos", description = "Lists all available ISO files.", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @@ -82,6 +87,9 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Parameter(name = ApiConstants.SHOW_UNIQUE, type = CommandType.BOOLEAN, description = "If set to true, list only unique isos across zones", since = "4.13.2") private Boolean showUnique; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource image for the isos") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -126,6 +134,10 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { return showUnique != null && showUnique; } + public Boolean getShowIcon () { + return showIcon != null ? showIcon : false; + } + public boolean listInReadyState() { Account account = CallContext.current().getCallingAccount(); // It is account specific if account is admin type and domainId and accountName are not null @@ -162,7 +174,21 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Override public void execute() { ListResponse response = _queryService.listIsos(this); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateIsoResponse(response.getResponses()); + } response.setResponseName(getCommandName()); setResponseObject(response); } + + private void updateIsoResponse(List 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); + } + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java index c69fc69ef31..5149d4e7af6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java @@ -19,7 +19,10 @@ package org.apache.cloudstack.api.command.user.network; import java.util.ArrayList; import java.util.List; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; import org.apache.cloudstack.api.response.NetworkOfferingResponse; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.RoleType; @@ -93,6 +96,10 @@ public class ListNetworksCmd extends BaseListTaggedResourcesCmd implements UserC @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "list networks by network offering ID") private Long networkOfferingId; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, + description = "flag to display the resource icon for networks") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -166,6 +173,11 @@ public class ListNetworksCmd extends BaseListTaggedResourcesCmd implements UserC } return super.getDisplay(); } + + public Boolean getShowIcon() { + return showIcon != null ? showIcon : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -186,5 +198,22 @@ public class ListNetworksCmd extends BaseListTaggedResourcesCmd implements UserC response.setResponses(networkResponses, networks.second()); response.setResponseName(getCommandName()); setResponseObject(response); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateNetworkResponse(response.getResponses()); + } + } + + private void updateNetworkResponse(List response) { + for (NetworkResponse networkResponse : response) { + ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Network, networkResponse.getId()); + if (resourceIcon == null) { + resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Vpc, networkResponse.getVpcId()); + if (resourceIcon == null) { + continue; + } + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + networkResponse.setResourceIconResponse(iconResponse); + } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java index 2c965512bba..f434b75559f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java @@ -21,6 +21,9 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -72,6 +75,10 @@ public class ListProjectsCmd extends BaseListAccountResourcesCmd { description = "comma separated list of project details requested, value can be a list of [ all, resource, min]") private List viewDetails; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, + description = "flag to display the resource icon for projects") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -124,6 +131,10 @@ public class ListProjectsCmd extends BaseListAccountResourcesCmd { return dv; } + public Boolean getShowIcon() { + return showIcon != null ? showIcon : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -133,5 +144,19 @@ public class ListProjectsCmd extends BaseListAccountResourcesCmd { ListResponse response = _queryService.listProjects(this); response.setResponseName(getCommandName()); this.setResponseObject(response); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateProjectResponse(response.getResponses()); + } + } + + private void updateProjectResponse(List response) { + for (ProjectResponse projectResponse : response) { + ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Project, projectResponse.getId()); + if (resourceIcon == null) { + continue; + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + projectResponse.setResourceIconResponse(iconResponse); + } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java index 5007e63f5c6..b7a46795638 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/ListDetailOptionsCmd.java @@ -54,12 +54,12 @@ public class ListDetailOptionsCmd extends BaseCmd { ///////////////////////////////////////////////////// public ResourceTag.ResourceObjectType getResourceType() { - return _taggedResourceService.getResourceType(resourceType); + return resourceManagerUtil.getResourceType(resourceType); } public String getResourceId() { if (!Strings.isNullOrEmpty(resourceId)) { - return _taggedResourceService.getUuid(resourceId, getResourceType()); + return resourceManagerUtil.getUuid(resourceId, getResourceType()); } return null; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/tag/CreateTagsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/tag/CreateTagsCmd.java index cde31cdec91..bc6817096b2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/tag/CreateTagsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/tag/CreateTagsCmd.java @@ -68,7 +68,7 @@ public class CreateTagsCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// public ResourceObjectType getResourceType() { - return _taggedResourceService.getResourceType(resourceType); + return resourceManagerUtil.getResourceType(resourceType); } public Map getTags() { @@ -106,17 +106,17 @@ public class CreateTagsCmd extends BaseAsyncCmd { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create tags"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to upload resource icon"); } } @Override public String getEventType() { - return EventTypes.EVENT_TAGS_CREATE; + return EventTypes.EVENT_RESOURCE_ICON_UPLOAD; } @Override public String getEventDescription() { - return "creating tags"; + return "Uploading resource icon"; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/tag/DeleteTagsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/tag/DeleteTagsCmd.java index e42cfce4d91..3fa6b678051 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/tag/DeleteTagsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/tag/DeleteTagsCmd.java @@ -66,7 +66,7 @@ public class DeleteTagsCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// public ResourceObjectType getResourceType() { - return _taggedResourceService.getResourceType(resourceType); + return resourceManagerUtil.getResourceType(resourceType); } public Map getTags() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java index 5ef66c50b98..bf3b2fbf5a4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java @@ -17,6 +17,9 @@ package org.apache.cloudstack.api.command.user.template; 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 org.apache.log4j.Logger; @@ -161,10 +164,17 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User return onlyReady; } + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource image for the templates") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// + public Boolean getShowIcon () { + return showIcon != null ? showIcon : false; + } + @Override public String getCommandName() { return s_name; @@ -178,10 +188,24 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User @Override public void execute() { ListResponse response = _queryService.listTemplates(this); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateTemplateResponse(response.getResponses()); + } response.setResponseName(getCommandName()); setResponseObject(response); } + private void updateTemplateResponse(List 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 getIds() { return ids; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java index 72839f253e1..b0ff1023c5c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java @@ -20,7 +20,10 @@ import java.util.ArrayList; import java.util.EnumSet; import java.util.List; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.log4j.Logger; @@ -141,6 +144,10 @@ public class ListVMsCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Parameter(name = ApiConstants.HA_ENABLE, type = CommandType.BOOLEAN, description = "list by the High Availability offering; true if filtering VMs with HA enabled; false for VMs with HA disabled", since = "4.15") private Boolean haEnabled; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, + description = "flag to display the resource icon for VMs", since = "4.16.0.0") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -252,6 +259,11 @@ public class ListVMsCmd extends BaseListTaggedResourcesCmd implements UserCmd { } return super.getDisplay(); } + + public Boolean getShowIcon() { + return showIcon != null ? showIcon : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -268,7 +280,30 @@ public class ListVMsCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Override public void execute() { ListResponse response = _queryService.searchForUserVMs(this); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateVMResponse(response.getResponses()); + } response.setResponseName(getCommandName()); setResponseObject(response); } + + protected void updateVMResponse(List 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; + } + resourceIcon = resourceIconManager.getByResourceTypeAndUuid(type, uuid); + if (resourceIcon == null) { + continue; + } + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + vmResponse.setResourceIconResponse(iconResponse); + } + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java index a8a3a75869d..d10bbb7016f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java @@ -61,7 +61,7 @@ public class AddResourceDetailCmd extends BaseAsyncCmd { } public ResourceTag.ResourceObjectType getResourceType() { - return _taggedResourceService.getResourceType(resourceType); + return resourceManagerUtil.getResourceType(resourceType); } public String getResourceId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListResourceDetailsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListResourceDetailsCmd.java index 83cb1fff901..33aa44a3422 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListResourceDetailsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListResourceDetailsCmd.java @@ -92,7 +92,7 @@ public class ListResourceDetailsCmd extends BaseListProjectAndAccountResourcesCm } public ResourceTag.ResourceObjectType getResourceType() { - return _taggedResourceService.getResourceType(resourceType); + return resourceManagerUtil.getResourceType(resourceType); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/RemoveResourceDetailCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/RemoveResourceDetailCmd.java index 6fe576dc63c..703fe8a1bda 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/RemoveResourceDetailCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/RemoveResourceDetailCmd.java @@ -56,7 +56,7 @@ public class RemoveResourceDetailCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// public ResourceTag.ResourceObjectType getResourceType() { - return _taggedResourceService.getResourceType(resourceType); + return resourceManagerUtil.getResourceType(resourceType); } public String getKey() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java index adcbf8bec6d..b230603f852 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java @@ -19,6 +19,8 @@ package org.apache.cloudstack.api.command.user.vpc; import java.util.ArrayList; import java.util.List; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -27,6 +29,7 @@ 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.ListResponse; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.VpcOfferingResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.ZoneResponse; @@ -76,6 +79,10 @@ public class ListVPCsCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, + description = "flag to display the resource icon for VPCs") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -124,6 +131,10 @@ public class ListVPCsCmd extends BaseListTaggedResourcesCmd implements UserCmd { return super.getDisplay(); } + public Boolean getShowIcon() { + return showIcon != null ? showIcon : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -144,6 +155,20 @@ public class ListVPCsCmd extends BaseListTaggedResourcesCmd implements UserCmd { response.setResponses(vpcResponses, vpcs.second()); response.setResponseName(getCommandName()); setResponseObject(response); + if (response != null && response.getCount() > 0 && getShowIcon()) { + updateVpcResponse(response.getResponses()); + } + } + + private void updateVpcResponse(List response) { + for (VpcResponse vpcResponse : response) { + ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Vpc, vpcResponse.getId()); + if (resourceIcon == null) { + continue; + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + vpcResponse.setResourceIconResponse(iconResponse); + } } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java index 9fd4c5a2dfc..0dba83b61b0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java @@ -123,7 +123,7 @@ public class AddVpnUserCmd extends BaseAsyncCreateCmd { if (!_ravService.applyVpnUsers(vpnUser.getAccountId(), userName)) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add vpn user"); } - }catch (Exception ex) { + } catch (Exception ex) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java index f7e3155535f..c79afb1c972 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java @@ -65,6 +65,9 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd { @Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "List zones by resource tags (key/value pairs)", since = "4.3") private Map tags; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource image for the zones") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -97,6 +100,10 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd { return TaggedResources.parseKeyValueMap(tags, false); } + public Boolean getShowIcon () { + return showIcon != null ? showIcon : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -108,7 +115,6 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd { @Override public void execute() { - ListResponse response = _queryService.listDataCenters(this); response.setResponseName(getCommandName()); setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java index 5cd2fd314e2..770df2678f2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java @@ -30,7 +30,7 @@ import com.cloud.user.Account; import com.google.gson.annotations.SerializedName; @EntityReference(value = Account.class) -public class AccountResponse extends BaseResponse implements ResourceLimitAndCountResponse { +public class AccountResponse extends BaseResponse implements ResourceLimitAndCountResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "the id of the account") private String id; @@ -263,6 +263,10 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou @Param(description = "the list of acl groups that account belongs to", since = "4.4") private List groups; + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") + ResourceIconResponse icon; + @Override public String getObjectId() { return id; @@ -537,4 +541,8 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou this.groups = groups; } + @Override + public void setResourceIconResponse(ResourceIconResponse icon) { + this.icon = icon; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java index 556fe04fd6f..6d5655269e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java @@ -28,7 +28,7 @@ import com.cloud.serializer.Param; import java.util.Date; @EntityReference(value = Domain.class) -public class DomainResponse extends BaseResponseWithAnnotations implements ResourceLimitAndCountResponse { +public class DomainResponse extends BaseResponseWithAnnotations implements ResourceLimitAndCountResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "the ID of the domain") private String id; @@ -175,6 +175,10 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou @SerializedName("secondarystorageavailable") @Param(description="the total secondary storage space (in GiB) available to be used for this domain", since="4.2.0") private String secondaryStorageAvailable; + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") + ResourceIconResponse icon; + public String getId() { return this.id; } @@ -429,4 +433,9 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou public void setVmRunning(Integer vmRunning) { // TODO Auto-generated method stub } + + @Override + public void setResourceIconResponse(ResourceIconResponse icon) { + this.icon = icon; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java index 80b1999eaca..4b41610ab02 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java @@ -33,7 +33,7 @@ import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") @EntityReference(value = {Network.class, ProjectAccount.class}) -public class NetworkResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse { +public class NetworkResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "the id of the network") @@ -247,6 +247,10 @@ public class NetworkResponse extends BaseResponseWithAnnotations implements Cont @Param(description = "If the network has redundant routers enabled", since = "4.11.1") private Boolean redundantRouter; + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") + ResourceIconResponse icon; + @SerializedName(ApiConstants.CREATED) @Param(description = "the date this network was created", since = "4.16.0") private Date created; @@ -271,6 +275,10 @@ public class NetworkResponse extends BaseResponseWithAnnotations implements Cont this.id = id; } + public String getId() { + return id; + } + public void setName(String name) { this.name = name; } @@ -428,6 +436,10 @@ public class NetworkResponse extends BaseResponseWithAnnotations implements Cont this.vpcId = vpcId; } + public String getVpcId() { + return vpcId; + } + public void setCanUseForDeploy(Boolean canUseForDeploy) { this.canUseForDeploy = canUseForDeploy; } @@ -496,6 +508,11 @@ public class NetworkResponse extends BaseResponseWithAnnotations implements Cont this.vpcName = vpcName; } + @Override + public void setResourceIconResponse(ResourceIconResponse icon) { + this.icon = icon; + } + public Date getCreated() { return created; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java index 7f14fce3007..c43dd09b127 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java @@ -30,7 +30,7 @@ import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @EntityReference(value = Project.class) -public class ProjectResponse extends BaseResponse implements ResourceLimitAndCountResponse { +public class ProjectResponse extends BaseResponse implements ResourceLimitAndCountResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "the id of the project") @@ -208,6 +208,10 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou @Param(description = "the total number of virtual machines running for this project", since = "4.2.0") private Integer vmRunning; + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") + ResourceIconResponse icon; + @SerializedName(ApiConstants.CREATED) @Param(description = "the date this project was created", since = "4.16.0") private Date created; @@ -216,6 +220,10 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou this.id = id; } + public String getId() { + return id; + } + public void setName(String name) { this.name = name; } @@ -427,6 +435,11 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou this.owners = owners; } + @Override + public void setResourceIconResponse(ResourceIconResponse icon) { + this.icon = icon; + } + public Date getCreated() { return created; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ResourceIconResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ResourceIconResponse.java new file mode 100644 index 00000000000..403a91c8894 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceIconResponse.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.cloud.server.ResourceTag; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +public class ResourceIconResponse extends BaseResponse { + @SerializedName(ApiConstants.RESOURCE_TYPE) + @Param(description = "resource type") + private ResourceTag.ResourceObjectType resourceType; + + @SerializedName(ApiConstants.RESOURCE_ID) + @Param(description = "id of the resource") + private String resourceId; + + @SerializedName(ApiConstants.BASE64_IMAGE) + @Param(description = "base64 representation of resource icon") + private String image; + + public ResourceTag.ResourceObjectType getResourceType() { + return resourceType; + } + + public void setResourceType(ResourceTag.ResourceObjectType resourceType) { + this.resourceType = resourceType; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SetResourceIconResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SetResourceIconResponse.java new file mode 100644 index 00000000000..9af1afea715 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/SetResourceIconResponse.java @@ -0,0 +1,21 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +public interface SetResourceIconResponse { + void setResourceIconResponse(ResourceIconResponse icon); +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java index 3633fa73de1..892b5b85262 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java @@ -34,7 +34,7 @@ import com.google.gson.annotations.SerializedName; @EntityReference(value = VirtualMachineTemplate.class) @SuppressWarnings("unused") -public class TemplateResponse extends BaseResponseWithTagInformation implements ControlledViewEntityResponse { +public class TemplateResponse extends BaseResponseWithTagInformation implements ControlledViewEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "the template ID") private String id; @@ -223,6 +223,10 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements @Param(description = "the URL which the template/iso is registered from") private String url; + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") + ResourceIconResponse icon; + public TemplateResponse() { tags = new LinkedHashSet<>(); } @@ -458,4 +462,9 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements public void setUrl(String url) { this.url = url; } + + @Override + public void setResourceIconResponse(ResourceIconResponse icon) { + this.icon = icon; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java index dd10510c03c..260d1602f82 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java @@ -29,7 +29,7 @@ import com.cloud.serializer.Param; import com.cloud.user.User; @EntityReference(value = User.class) -public class UserResponse extends BaseResponse { +public class UserResponse extends BaseResponse implements SetResourceIconResponse { @SerializedName("id") @Param(description = "the user ID") private String id; @@ -115,6 +115,10 @@ public class UserResponse extends BaseResponse { @Param(description = "true if user is default, false otherwise", since = "4.2.0") private Boolean isDefault; + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") + ResourceIconResponse icon; + @Override public String getObjectId() { return this.getId(); @@ -275,4 +279,9 @@ public class UserResponse extends BaseResponse { this.userSource = User.Source.NATIVE.toString().toLowerCase(); } } + + @Override + public void setResourceIconResponse(ResourceIconResponse icon) { + this.icon = icon; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java index 11c1581f2af..3483c17f474 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java @@ -37,7 +37,7 @@ import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") @EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class}) -public class UserVmResponse extends BaseResponseWithTagInformation implements ControlledEntityResponse { +public class UserVmResponse extends BaseResponseWithTagInformation implements ControlledEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "the ID of the virtual machine") private String id; @@ -328,6 +328,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "the total number of network traffic bytes sent") private Long bytesSent; + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") + ResourceIconResponse resourceIconResponse; + public UserVmResponse() { securityGroupList = new LinkedHashSet(); nics = new TreeSet<>(Comparator.comparingInt(x -> Integer.parseInt(x.getDeviceId()))); @@ -924,6 +928,15 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co public void setPoolType(String poolType) { this.poolType = poolType; } + @Override + public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) { + this.resourceIconResponse = resourceIconResponse; + } + + public ResourceIconResponse getResourceIconResponse() { + return resourceIconResponse; + } + public void setLastUpdated(Date lastUpdated) { this.lastUpdated = lastUpdated; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java index f91945f3bcf..151c3a9561b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java @@ -30,7 +30,7 @@ import com.google.gson.annotations.SerializedName; @EntityReference(value = Vpc.class) @SuppressWarnings("unused") -public class VpcResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse { +public class VpcResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse, SetResourceIconResponse { @SerializedName("id") @Param(description = "the id of the VPC") private String id; @@ -127,10 +127,18 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll @Param(description = "if this VPC has redundant router", since = "4.6") private boolean redundantRouter; + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") + ResourceIconResponse icon; + public void setId(final String id) { this.id = id; } + public String getId() { + return id; + } + public void setName(final String name) { this.name = name; } @@ -231,4 +239,9 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll public void setRedundantRouter(final Boolean redundantRouter) { this.redundantRouter = redundantRouter; } + + @Override + public void setResourceIconResponse(ResourceIconResponse icon) { + this.icon = icon; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java index 47f3b0d050c..676ca4a7dbf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java @@ -32,7 +32,7 @@ import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") @EntityReference(value = DataCenter.class) -public class ZoneResponse extends BaseResponseWithAnnotations { +public class ZoneResponse extends BaseResponseWithAnnotations implements SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "Zone id") private String id; @@ -125,6 +125,10 @@ public class ZoneResponse extends BaseResponseWithAnnotations { @Param(description = "Meta data associated with the zone (key/value pairs)", since = "4.3.0") private Map resourceDetails; + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") + ResourceIconResponse resourceIconResponse; + public ZoneResponse() { tags = new LinkedHashSet(); } @@ -315,4 +319,13 @@ public class ZoneResponse extends BaseResponseWithAnnotations { public Map getResourceDetails() { return resourceDetails; } + + @Override + public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) { + this.resourceIconResponse = resourceIconResponse; + } + + public ResourceIconResponse getResourceIconResponse() { + return resourceIconResponse; + } } diff --git a/api/src/main/java/org/apache/cloudstack/query/QueryService.java b/api/src/main/java/org/apache/cloudstack/query/QueryService.java index 3484de84ef4..bb418f98408 100644 --- a/api/src/main/java/org/apache/cloudstack/query/QueryService.java +++ b/api/src/main/java/org/apache/cloudstack/query/QueryService.java @@ -24,6 +24,7 @@ import org.apache.cloudstack.api.command.admin.host.ListHostTagsCmd; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; import org.apache.cloudstack.api.command.admin.internallb.ListInternalLBVMsCmd; import org.apache.cloudstack.api.command.admin.management.ListMgmtsCmd; +import org.apache.cloudstack.api.command.admin.resource.icon.ListResourceIconCmd; import org.apache.cloudstack.api.command.admin.router.GetRouterHealthCheckResultsCmd; import org.apache.cloudstack.api.command.admin.router.ListRoutersCmd; import org.apache.cloudstack.api.command.admin.storage.ListImageStoresCmd; @@ -67,6 +68,7 @@ import org.apache.cloudstack.api.response.ProjectAccountResponse; import org.apache.cloudstack.api.response.ProjectInvitationResponse; import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.ResourceDetailResponse; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.ResourceTagResponse; import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; @@ -159,6 +161,8 @@ public interface QueryService { DetailOptionsResponse listDetailOptions(ListDetailOptionsCmd cmd); + ListResponse listResourceIcons(ListResourceIconCmd cmd); + ListResponse searchForAffinityGroups(ListAffinityGroupsCmd cmd); List listResourceDetails(ListResourceDetailsCmd cmd); diff --git a/engine/schema/src/main/java/com/cloud/resource/icon/ResourceIconVO.java b/engine/schema/src/main/java/com/cloud/resource/icon/ResourceIconVO.java new file mode 100644 index 00000000000..fb9e3935d0e --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/resource/icon/ResourceIconVO.java @@ -0,0 +1,167 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.resource.icon; + +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.GenerationType; +import javax.persistence.Column; +import javax.persistence.Enumerated; +import javax.persistence.EnumType; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "resource_icon") +public class ResourceIconVO implements ResourceIcon { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "resource_id") + long resourceId; + + @Column(name = "resource_uuid") + private String resourceUuid; + + @Column(name = "resource_type") + @Enumerated(value = EnumType.STRING) + private ResourceTag.ResourceObjectType resourceType; + + @Column(name = "icon", length = 65535) + private String icon; + + @Column(name = "created") + @Temporal(value = TemporalType.TIMESTAMP) + private Date created = null; + + @Column(name = "updated") + @Temporal(value = TemporalType.TIMESTAMP) + Date updated; + + @Column(name = "removed") + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + + protected ResourceIconVO() { + uuid = UUID.randomUUID().toString(); + } + + public ResourceIconVO(long resourceId, ResourceTag.ResourceObjectType resourceType, String resourceUuid, String icon) { + super(); + this.resourceId = resourceId; + this.resourceType = resourceType; + uuid = UUID.randomUUID().toString(); + this.resourceUuid = resourceUuid; + this.icon = icon; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public long getResourceId() { + return resourceId; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + public String getResourceUuid() { + return resourceUuid; + } + + public void setResourceUuid(String resourceUuid) { + this.resourceUuid = resourceUuid; + } + + public ResourceTag.ResourceObjectType getResourceType() { + return resourceType; + } + + public void setResourceType(ResourceTag.ResourceObjectType resourceType) { + this.resourceType = resourceType; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getUpdated() { + return updated; + } + + public void setUpdated(Date updated) { + this.updated = updated; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + @Override + public String toString() { + return "ResourceIconVO{" + + "id=" + id + + ", uuid='" + uuid + '\'' + + ", resourceId=" + resourceId + + ", resourceUuid='" + resourceUuid + '\'' + + ", resourceType=" + resourceType + + '}'; + } +} diff --git a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java new file mode 100644 index 00000000000..3724e03d9d0 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.resource.icon.dao; + +import com.cloud.resource.icon.ResourceIconVO; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.response.ResourceIconResponse; + +import java.util.List; + +public interface ResourceIconDao extends GenericDao { + ResourceIconResponse newResourceIconResponse(ResourceIcon resourceIconVO); + ResourceIconVO findByResourceUuid(String resourceUuid, ResourceTag.ResourceObjectType resourceType); + List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType); +} diff --git a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java new file mode 100644 index 00000000000..41eba40c01f --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java @@ -0,0 +1,79 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.resource.icon.dao; + +import com.cloud.resource.icon.ResourceIconVO; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; +import com.cloud.utils.db.GenericDaoBase; +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.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +public class ResourceIconDaoImpl extends GenericDaoBase implements ResourceIconDao { + public static final Logger s_logger = Logger.getLogger(ResourceIconDaoImpl.class); + private final SearchBuilder AllFieldsSearch; + + protected ResourceIconDaoImpl() { + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("resourceId", AllFieldsSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("uuid", AllFieldsSearch.entity().getResourceUuid(), SearchCriteria.Op.IN); + AllFieldsSearch.and("resourceType", AllFieldsSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + AllFieldsSearch.done(); + } + + @Override + public ResourceIconResponse newResourceIconResponse(ResourceIcon resourceIcon) { + ResourceIconResponse resourceIconResponse = new ResourceIconResponse(); + resourceIconResponse.setResourceId(resourceIcon.getResourceUuid()); + resourceIconResponse.setResourceType(resourceIcon.getResourceType()); + resourceIconResponse.setImage(resourceIcon.getIcon()); + resourceIconResponse.setObjectName(ApiConstants.RESOURCE_ICON); + return resourceIconResponse; + } + + @Override + public ResourceIconVO findByResourceUuid(String resourceUuid, ResourceTag.ResourceObjectType resourceType) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("uuid", (Object[]) new String[]{resourceUuid}); + sc.setParameters("resourceType", resourceType); + return findOneBy(sc); + } + + @Override + public List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("uuid", resourceUuids.toArray()); + sc.setParameters("resourceType", resourceType); + List resourceIcons = listBy(sc); + List iconResponses = new ArrayList<>(); + for (ResourceIconVO resourceIcon : resourceIcons) { + ResourceIconResponse response = new ResourceIconResponse(); + response.setResourceId(resourceIcon.getResourceUuid()); + response.setResourceType(resourceIcon.getResourceType()); + response.setImage(resourceIcon.getIcon()); + response.setObjectName(ApiConstants.RESOURCE_ICON); + iconResponses.add(response); + } + return iconResponses; + } +} diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 362054ece34..508b01c2b57 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -173,6 +173,7 @@ + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41520to41600.sql b/engine/schema/src/main/resources/META-INF/db/schema-41520to41600.sql index cd595ee46a0..64c381e0e7a 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41520to41600.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41520to41600.sql @@ -697,6 +697,20 @@ CREATE VIEW `cloud`.`host_view` AS GROUP BY `host`.`id`; +CREATE TABLE `cloud`.`resource_icon` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40), + `icon` blob COMMENT 'Base64 version of the resource icon', + `resource_id` bigint unsigned NOT NULL, + `resource_uuid` varchar(40), + `resource_type` varchar(255), + `updated` datetime default NULL, + `created` datetime default NULL, + `removed` datetime default NULL, + PRIMARY KEY (`id`), + CONSTRAINT `uc_resource_icon__uuid` UNIQUE (`uuid`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + ALTER TABLE `cloud`.`annotations` ADD COLUMN `admins_only` tinyint(1) unsigned NOT NULL DEFAULT 1; -- Allow annotations for resource admins, domain admins and users diff --git a/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVMsMetricsCmd.java b/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVMsMetricsCmd.java index 9b8564c2c8b..947c2f99ba9 100644 --- a/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVMsMetricsCmd.java +++ b/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVMsMetricsCmd.java @@ -44,6 +44,7 @@ public class ListVMsMetricsCmd extends ListVMsCmd { @Override public void execute() { ListResponse userVms = _queryService.searchForUserVMs(this); + updateVMResponse(userVms.getResponses()); final List metricsResponses = metricsService.listVmMetrics(userVms.getResponses()); ListResponse response = new ListResponse<>(); response.setResponses(metricsResponses, userVms.getCount()); diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index d688c276a33..dd46751552a 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -27,6 +27,9 @@ import java.util.Set; import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.resource.icon.ResourceIconVO; +import com.cloud.resource.icon.dao.ResourceIconDao; +import com.cloud.server.ResourceIcon; import org.apache.cloudstack.acl.Role; import org.apache.cloudstack.acl.RoleService; import org.apache.cloudstack.affinity.AffinityGroup; @@ -55,6 +58,7 @@ import org.apache.cloudstack.api.response.NetworkOfferingResponse; import org.apache.cloudstack.api.response.ProjectAccountResponse; import org.apache.cloudstack.api.response.ProjectInvitationResponse; import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.ResourceTagResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; @@ -258,6 +262,7 @@ import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.StatsCollector; import com.cloud.server.TaggedResourceService; +import com.cloud.server.ResourceManagerUtil; import com.cloud.service.ServiceOfferingDetailsVO; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; @@ -418,6 +423,7 @@ public class ApiDBUtils { static AutoScaleVmGroupDao s_asVmGroupDao; static CounterDao s_counterDao; static ResourceTagJoinDao s_tagJoinDao; + static ResourceIconDao s_resourceIconDao; static EventJoinDao s_eventJoinDao; static InstanceGroupJoinDao s_vmGroupJoinDao; static UserAccountJoinDao s_userAccountJoinDao; @@ -462,6 +468,7 @@ public class ApiDBUtils { static BackupScheduleDao s_backupScheduleDao; static BackupOfferingDao s_backupOfferingDao; static NicDao s_nicDao; + static ResourceManagerUtil s_resourceManagerUtil; @Inject private ManagementServer ms; @@ -708,6 +715,10 @@ public class ApiDBUtils { private BackupScheduleDao backupScheduleDao; @Inject private NicDao nicDao; + @Inject + private ResourceIconDao resourceIconDao; + @Inject + private ResourceManagerUtil resourceManagerUtil; @PostConstruct void init() { @@ -834,6 +845,8 @@ public class ApiDBUtils { s_backupDao = backupDao; s_backupScheduleDao = backupScheduleDao; s_backupOfferingDao = backupOfferingDao; + s_resourceIconDao = resourceIconDao; + s_resourceManagerUtil = resourceManagerUtil; } // /////////////////////////////////////////////////////////// @@ -1484,7 +1497,7 @@ public class ApiDBUtils { } public static String getUuid(String resourceId, ResourceObjectType resourceType) { - return s_taggedResourceService.getUuid(resourceId, resourceType); + return s_resourceManagerUtil.getUuid(resourceId, resourceType); } public static List listByResourceTypeAndId(ResourceObjectType type, long resourceId) { @@ -1800,6 +1813,10 @@ public class ApiDBUtils { return s_tagJoinDao.newResourceTagResponse(vsg, keyValueOnly); } + public static ResourceIconResponse newResourceIconResponse(ResourceIcon resourceIcon) { + return s_resourceIconDao.newResourceIconResponse(resourceIcon); + } + public static ResourceTagJoinVO newResourceTagView(ResourceTag sg) { return s_tagJoinDao.newResourceTagView(sg); } @@ -2001,8 +2018,8 @@ public class ApiDBUtils { return s_serviceOfferingJoinDao.newServiceOfferingView(offering); } - public static ZoneResponse newDataCenterResponse(ResponseView view, DataCenterJoinVO dc, Boolean showCapacities) { - return s_dcJoinDao.newDataCenterResponse(view, dc, showCapacities); + public static ZoneResponse newDataCenterResponse(ResponseView view, DataCenterJoinVO dc, Boolean showCapacities, Boolean showResourceImage) { + return s_dcJoinDao.newDataCenterResponse(view, dc, showCapacities, showResourceImage); } public static DataCenterJoinVO newDataCenterView(DataCenter dc) { @@ -2081,6 +2098,10 @@ public class ApiDBUtils { return s_tagJoinDao.listBy(resourceUUID, resourceType); } + public static ResourceIconVO getResourceIconByResourceUUID(String resourceUUID, ResourceObjectType resourceType) { + return s_resourceIconDao.findByResourceUuid(resourceUUID, resourceType); + } + public static BackupResponse newBackupResponse(Backup backup) { return s_backupDao.newBackupResponse(backup); } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 09057e64277..bb79998b306 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -33,6 +33,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import com.cloud.server.ResourceIcon; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; @@ -113,6 +114,7 @@ import org.apache.cloudstack.api.response.ProviderResponse; import org.apache.cloudstack.api.response.RegionResponse; import org.apache.cloudstack.api.response.RemoteAccessVpnResponse; import org.apache.cloudstack.api.response.ResourceCountResponse; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.ResourceLimitResponse; import org.apache.cloudstack.api.response.ResourceTagResponse; import org.apache.cloudstack.api.response.RollingMaintenanceHostSkippedResponse; @@ -1151,9 +1153,9 @@ public class ApiResponseHelper implements ResponseGenerator { } @Override - public ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities) { + public ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities, Boolean showResourceIcon) { DataCenterJoinVO vOffering = ApiDBUtils.newDataCenterView(dataCenter); - return ApiDBUtils.newDataCenterResponse(view, vOffering, showCapacities); + return ApiDBUtils.newDataCenterResponse(view, vOffering, showCapacities, showResourceIcon); } public static List getDataCenterCapacityResponse(Long zoneId) { @@ -4457,4 +4459,9 @@ public class ApiResponseHelper implements ResponseGenerator { response.setObjectName("rollingmaintenance"); return response; } + + @Override + public ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon) { + return ApiDBUtils.newResourceIconResponse(resourceIcon); + } } diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 2041e9207f6..f61fa337a03 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -30,6 +30,10 @@ import java.util.stream.Stream; import javax.inject.Inject; +import com.cloud.resource.icon.dao.ResourceIconDao; +import com.cloud.server.ResourceManagerUtil; +import com.cloud.storage.dao.VMTemplateDetailsDao; +import com.cloud.vm.VirtualMachineManager; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -48,6 +52,7 @@ import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; import org.apache.cloudstack.api.command.admin.internallb.ListInternalLBVMsCmd; import org.apache.cloudstack.api.command.admin.iso.ListIsosCmdByAdmin; import org.apache.cloudstack.api.command.admin.management.ListMgmtsCmd; +import org.apache.cloudstack.api.command.admin.resource.icon.ListResourceIconCmd; import org.apache.cloudstack.api.command.admin.router.GetRouterHealthCheckResultsCmd; import org.apache.cloudstack.api.command.admin.router.ListRoutersCmd; import org.apache.cloudstack.api.command.admin.storage.ListImageStoresCmd; @@ -93,6 +98,7 @@ import org.apache.cloudstack.api.response.ProjectAccountResponse; import org.apache.cloudstack.api.response.ProjectInvitationResponse; import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.ResourceDetailResponse; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.ResourceTagResponse; import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; @@ -217,7 +223,6 @@ import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.dao.StoragePoolTagsDao; import com.cloud.storage.dao.VMTemplateDao; -import com.cloud.storage.dao.VMTemplateDetailsDao; import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.template.VirtualMachineTemplate.State; @@ -244,7 +249,6 @@ import com.cloud.vm.DomainRouterVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VmDetailConstants; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.UserVmDao; @@ -383,6 +387,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Inject private TaggedResourceService _taggedResourceMgr; + @Inject + private ResourceManagerUtil resourceManagerUtil; + @Inject private AffinityGroupVMMapDao _affinityGroupVMMapDao; @@ -437,6 +444,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Inject private VirtualMachineManager virtualMachineManager; + @Inject + private ResourceIconDao resourceIconDao; + /* * (non-Javadoc) * @@ -3145,7 +3155,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q respView = ResponseView.Full; } - List dcResponses = ViewResponseHelper.createDataCenterResponse(respView, cmd.getShowCapacities(), result.first().toArray(new DataCenterJoinVO[result.first().size()])); + List dcResponses = ViewResponseHelper.createDataCenterResponse(respView, cmd.getShowCapacities(), cmd.getShowIcon(), result.first().toArray(new DataCenterJoinVO[result.first().size()])); response.setResponses(dcResponses, result.second()); return response; } @@ -3792,6 +3802,13 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q return new DetailOptionsResponse(options); } + @Override + public ListResponse listResourceIcons(ListResourceIconCmd cmd) { + ListResponse responses = new ListResponse<>(); + responses.setResponses(resourceIconDao.listResourceIcons(cmd.getResourceIds(), cmd.getResourceType())); + return responses; + } + private void fillVMOrTemplateDetailOptions(final Map> options, final HypervisorType hypervisorType) { if (options == null) { throw new CloudRuntimeException("Invalid/null detail-options response object passed"); @@ -4066,7 +4083,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q //Validation - 1.3 if (resourceIdStr != null) { - resourceId = _taggedResourceMgr.getResourceId(resourceIdStr, resourceType); + resourceId = resourceManagerUtil.getResourceId(resourceIdStr, resourceType); if (resourceId == null) { throw new InvalidParameterValueException("Cannot find resource with resourceId " + resourceIdStr + " and of resource type " + resourceType); } @@ -4102,7 +4119,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q protected ResourceDetailResponse createResourceDetailsResponse(ResourceDetail requestedDetail, ResourceTag.ResourceObjectType resourceType) { ResourceDetailResponse resourceDetailResponse = new ResourceDetailResponse(); - resourceDetailResponse.setResourceId(_taggedResourceMgr.getUuid(String.valueOf(requestedDetail.getResourceId()), resourceType)); + resourceDetailResponse.setResourceId(resourceManagerUtil.getUuid(String.valueOf(requestedDetail.getResourceId()), resourceType)); resourceDetailResponse.setName(requestedDetail.getName()); resourceDetailResponse.setValue(requestedDetail.getValue()); resourceDetailResponse.setForDisplay(requestedDetail.isDisplay()); diff --git a/server/src/main/java/com/cloud/api/query/ViewResponseHelper.java b/server/src/main/java/com/cloud/api/query/ViewResponseHelper.java index ec3397407f2..ed6f9514ab9 100644 --- a/server/src/main/java/com/cloud/api/query/ViewResponseHelper.java +++ b/server/src/main/java/com/cloud/api/query/ViewResponseHelper.java @@ -560,10 +560,10 @@ public class ViewResponseHelper { return respList; } - public static List createDataCenterResponse(ResponseView view, Boolean showCapacities, DataCenterJoinVO... dcs) { + public static List createDataCenterResponse(ResponseView view, Boolean showCapacities, Boolean showResourceImage, DataCenterJoinVO... dcs) { List respList = new ArrayList(); for (DataCenterJoinVO vt : dcs){ - respList.add(ApiDBUtils.newDataCenterResponse(view, vt, showCapacities)); + respList.add(ApiDBUtils.newDataCenterResponse(view, vt, showCapacities, showResourceImage)); } return respList; } diff --git a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDao.java index 1c3ff1b2741..a53f86495e7 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDao.java +++ b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDao.java @@ -25,7 +25,7 @@ import com.cloud.utils.db.GenericDao; public interface DataCenterJoinDao extends GenericDao { - ZoneResponse newDataCenterResponse(ResponseView view, DataCenterJoinVO dof, Boolean showCapacities); + ZoneResponse newDataCenterResponse(ResponseView view, DataCenterJoinVO dof, Boolean showCapacities, Boolean showResourceImage); DataCenterJoinVO newDataCenterView(DataCenter dof); } diff --git a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java index c777e65f171..3762484f176 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java @@ -20,6 +20,8 @@ import java.util.List; import javax.inject.Inject; +import com.cloud.resource.icon.ResourceIconVO; +import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.context.CallContext; @@ -61,7 +63,7 @@ public class DataCenterJoinDaoImpl extends GenericDaoBase> s_daoMap = new HashMap>(); @@ -189,7 +192,7 @@ public class ResourceMetaDataManagerImpl extends ManagerBase implements Resource } DetailDaoHelper newDetailDaoHelper = new DetailDaoHelper(resourceType); - newDetailDaoHelper.addDetail(_taggedResourceMgr.getResourceId(resourceId, resourceType), key, value, forDisplay); + newDetailDaoHelper.addDetail(resourceManagerUtil.getResourceId(resourceId, resourceType), key, value, forDisplay); } return true; @@ -201,7 +204,7 @@ public class ResourceMetaDataManagerImpl extends ManagerBase implements Resource @DB @ActionEvent(eventType = EventTypes.EVENT_RESOURCE_DETAILS_DELETE, eventDescription = "deleting resource meta data") public boolean deleteResourceMetaData(String resourceId, ResourceObjectType resourceType, String key) { - long id = _taggedResourceMgr.getResourceId(resourceId, resourceType); + long id = resourceManagerUtil.getResourceId(resourceId, resourceType); DetailDaoHelper newDetailDaoHelper = new DetailDaoHelper(resourceType); if (key != null) { diff --git a/server/src/main/java/com/cloud/resourceicon/ResourceIconManagerImpl.java b/server/src/main/java/com/cloud/resourceicon/ResourceIconManagerImpl.java new file mode 100644 index 00000000000..137f655459b --- /dev/null +++ b/server/src/main/java/com/cloud/resourceicon/ResourceIconManagerImpl.java @@ -0,0 +1,230 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.resourceicon; + +import com.cloud.domain.PartOf; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.metadata.ResourceMetaDataManagerImpl; +import com.cloud.network.security.SecurityGroupRuleVO; +import com.cloud.network.security.SecurityGroupVO; +import com.cloud.network.vpc.NetworkACLItemVO; +import com.cloud.network.vpc.NetworkACLVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.projects.ProjectVO; +import com.cloud.resource.icon.dao.ResourceIconDao; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceIconManager; + +import com.cloud.server.ResourceManagerUtil; +import com.cloud.server.ResourceTag; +import com.cloud.resource.icon.ResourceIconVO; +import com.cloud.storage.SnapshotPolicyVO; +import com.cloud.storage.VolumeVO; +import com.cloud.tags.ResourceManagerUtilImpl; +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.AccountVO; +import com.cloud.user.OwnedBy; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import javax.persistence.EntityExistsException; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +public class ResourceIconManagerImpl extends ManagerBase implements ResourceIconManager { + public static final Logger s_logger = Logger.getLogger(ResourceMetaDataManagerImpl.class); + + @Inject + AccountService accountService; + @Inject + ResourceManagerUtil resourceManagerUtil; + @Inject + ResourceIconDao resourceIconDao; + @Inject + EntityManager entityMgr; + @Inject + AccountDao accountDao; + + private Pair getAccountDomain(long resourceId, ResourceTag.ResourceObjectType resourceType) { + Class clazz = ResourceManagerUtilImpl.s_typeMap.get(resourceType); + + Object entity = entityMgr.findById(clazz, resourceId); + Long accountId = null; + Long domainId = null; + + // if the resource type is a security group rule, get the accountId and domainId from the security group itself + if (resourceType == ResourceTag.ResourceObjectType.SecurityGroupRule) { + SecurityGroupRuleVO rule = (SecurityGroupRuleVO)entity; + Object SecurityGroup = entityMgr.findById(ResourceManagerUtilImpl.s_typeMap.get(ResourceTag.ResourceObjectType.SecurityGroup), rule.getSecurityGroupId()); + + accountId = ((SecurityGroupVO)SecurityGroup).getAccountId(); + domainId = ((SecurityGroupVO)SecurityGroup).getDomainId(); + } + + if (resourceType == ResourceTag.ResourceObjectType.Account) { + AccountVO account = (AccountVO)entity; + accountId = account.getId(); + domainId = account.getDomainId(); + } + + // if the resource type is network acl, get the accountId and domainId from VPC following: NetworkACLItem -> NetworkACL -> VPC + if (resourceType == ResourceTag.ResourceObjectType.NetworkACL) { + NetworkACLItemVO aclItem = (NetworkACLItemVO)entity; + Object networkACL = entityMgr.findById(ResourceManagerUtilImpl.s_typeMap.get(ResourceTag.ResourceObjectType.NetworkACLList), aclItem.getAclId()); + Long vpcId = ((NetworkACLVO)networkACL).getVpcId(); + + if (vpcId != null && vpcId != 0) { + Object vpc = entityMgr.findById(ResourceManagerUtilImpl.s_typeMap.get(ResourceTag.ResourceObjectType.Vpc), vpcId); + + accountId = ((VpcVO)vpc).getAccountId(); + domainId = ((VpcVO)vpc).getDomainId(); + } + } + + if (resourceType == ResourceTag.ResourceObjectType.Project) { + accountId = ((ProjectVO)entity).getProjectAccountId(); + } + + if (resourceType == ResourceTag.ResourceObjectType.SnapshotPolicy) { + accountId = entityMgr.findById(VolumeVO.class, ((SnapshotPolicyVO)entity).getVolumeId()).getAccountId(); + } + + if (entity instanceof OwnedBy) { + accountId = ((OwnedBy)entity).getAccountId(); + } + + if (entity instanceof PartOf) { + domainId = ((PartOf)entity).getDomainId(); + } + + if (accountId == null) { + accountId = Account.ACCOUNT_ID_SYSTEM; + } + + if ((domainId == null) || ((accountId != null) && (domainId.longValue() == -1))) { + domainId = accountDao.getDomainIdForGivenAccountId(accountId); + } + return new Pair<>(accountId, domainId); + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_RESOURCE_ICON_UPLOAD, eventDescription = "uploading resource icon") + public boolean uploadResourceIcon(List resourceIds, ResourceTag.ResourceObjectType resourceType, String base64Image) { + final Account caller = CallContext.current().getCallingAccount(); + + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + for (String resourceId : resourceIds) { + if (!resourceType.resourceIconSupport()) { + throw new InvalidParameterValueException("The resource type " + resourceType + " doesn't support resource icons"); + } + + if (base64Image == null) { + throw new InvalidParameterValueException("No icon provided to be uploaded for resource: " + resourceId); + } + + long id = resourceManagerUtil.getResourceId(resourceId, resourceType); + String resourceUuid = resourceManagerUtil.getUuid(resourceId, resourceType); + ResourceIconVO existingResourceIcon = resourceIconDao.findByResourceUuid(resourceUuid, resourceType); + ResourceIconVO resourceIcon = null; + Pair accountDomainPair = getAccountDomain(id, resourceType); + Long domainId = accountDomainPair.second(); + Long accountId = accountDomainPair.first(); + resourceManagerUtil.checkResourceAccessible(accountId, domainId, String.format("Account ' %s ' doesn't have permissions to upload icon for resource ' %s ", caller, id)); + + if (existingResourceIcon == null) { + resourceIcon = new ResourceIconVO(id, resourceType, resourceUuid, base64Image); + } else { + resourceIcon = existingResourceIcon; + resourceIcon.setIcon(base64Image); + resourceIcon.setUpdated(new Date()); + } + try { + resourceIconDao.persist(resourceIcon); + } catch (EntityExistsException e) { + throw new CloudRuntimeException(String.format("Image already uploaded for resource type: %s with id %s", resourceType.toString(), resourceId),e); + } + } + } + }); + + return true; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_RESOURCE_ICON_DELETE, eventDescription = "deleting resource icon") + public boolean deleteResourceIcon(List resourceIds, ResourceTag.ResourceObjectType resourceType) { + Account caller = CallContext.current().getCallingAccount(); + List resourceIcons = searchResourceIcons(resourceIds, resourceType); + if (resourceIcons.isEmpty()) { + s_logger.debug("No resource Icon(s) uploaded for the specified resources"); + return false; + } + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + for (ResourceIcon resourceIcon : resourceIcons) { + String resourceId = resourceIcon.getResourceUuid(); + long id = resourceManagerUtil.getResourceId(resourceId, resourceType); + Pair accountDomainPair = getAccountDomain(id, resourceType); + Long domainId = accountDomainPair.second(); + Long accountId = accountDomainPair.first(); + resourceManagerUtil.checkResourceAccessible(accountId, domainId, String.format("Account ' %s ' doesn't have permissions to upload icon for resource ' %s ", caller, id)); + resourceIconDao.remove(resourceIcon.getId()); + s_logger.debug("Removed icon for resources (" + + String.join(", ", resourceIds) + ")"); + } + } + }); + return true; + } + + @Override + public ResourceIcon getByResourceTypeAndUuid(ResourceTag.ResourceObjectType type, String resourceId) { + return resourceIconDao.findByResourceUuid(resourceId, type); + } + + private List searchResourceIcons(List resourceIds, ResourceTag.ResourceObjectType resourceType) { + List resourceUuids = resourceIds.stream().map(resourceId -> resourceManagerUtil.getUuid(resourceId, resourceType)).collect(Collectors.toList()); + SearchBuilder sb = resourceIconDao.createSearchBuilder(); + sb.and("resourceUuid", sb.entity().getResourceUuid(), SearchCriteria.Op.IN); + sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ); + + SearchCriteria sc = sb.create(); + sc.setParameters("resourceUuid", resourceUuids.toArray()); + sc.setParameters("resourceType", resourceType); + return resourceIconDao.search(sc, null); + } +} diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 9f419e0d897..f7cd7de3337 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -179,6 +179,9 @@ import org.apache.cloudstack.api.command.admin.resource.ListAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.ListCapacityCmd; import org.apache.cloudstack.api.command.admin.resource.StartRollingMaintenanceCmd; import org.apache.cloudstack.api.command.admin.resource.UploadCustomCertificateCmd; +import org.apache.cloudstack.api.command.admin.resource.icon.DeleteResourceIconCmd; +import org.apache.cloudstack.api.command.admin.resource.icon.ListResourceIconCmd; +import org.apache.cloudstack.api.command.admin.resource.icon.UploadResourceIconCmd; import org.apache.cloudstack.api.command.admin.router.ConfigureOvsElementCmd; import org.apache.cloudstack.api.command.admin.router.ConfigureVirtualRouterElementCmd; import org.apache.cloudstack.api.command.admin.router.CreateVirtualRouterElementCmd; @@ -3446,6 +3449,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(GetRouterHealthCheckResultsCmd.class); cmdList.add(StartRollingMaintenanceCmd.class); cmdList.add(MigrateSecondaryStorageDataCmd.class); + cmdList.add(UploadResourceIconCmd.class); + cmdList.add(DeleteResourceIconCmd.class); + cmdList.add(ListResourceIconCmd.class); // Out-of-band management APIs for admins cmdList.add(EnableOutOfBandManagementForHostCmd.class); diff --git a/server/src/main/java/com/cloud/tags/ResourceManagerUtilImpl.java b/server/src/main/java/com/cloud/tags/ResourceManagerUtilImpl.java new file mode 100644 index 00000000000..e088b26ad96 --- /dev/null +++ b/server/src/main/java/com/cloud/tags/ResourceManagerUtilImpl.java @@ -0,0 +1,186 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.tags; + +import com.cloud.dc.DataCenterVO; +import com.cloud.domain.DomainVO; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.network.LBHealthCheckPolicyVO; +import com.cloud.network.as.AutoScaleVmGroupVO; +import com.cloud.network.as.AutoScaleVmProfileVO; + +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.PortForwardingRuleVO; +import com.cloud.network.security.SecurityGroupRuleVO; +import com.cloud.network.security.SecurityGroupVO; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.LBStickinessPolicyVO; +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.RemoteAccessVpnVO; +import com.cloud.network.dao.Site2SiteCustomerGatewayVO; +import com.cloud.network.dao.Site2SiteVpnConnectionVO; +import com.cloud.network.dao.Site2SiteVpnGatewayVO; +import com.cloud.network.vpc.NetworkACLItemVO; +import com.cloud.network.vpc.NetworkACLVO; +import com.cloud.network.vpc.StaticRouteVO; +import com.cloud.network.vpc.VpcOfferingVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.projects.ProjectVO; +import com.cloud.server.ResourceManagerUtil; +import com.cloud.server.ResourceTag; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.SnapshotPolicyVO; + +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.DomainManager; +import com.cloud.user.UserVO; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicVO; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.snapshot.VMSnapshotVO; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.commons.lang.StringUtils; + +import javax.inject.Inject; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class ResourceManagerUtilImpl implements ResourceManagerUtil { + public static final Map> s_typeMap = new HashMap<>(); + + static { + s_typeMap.put(ResourceTag.ResourceObjectType.UserVm, UserVmVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Volume, VolumeVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Template, VMTemplateVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.ISO, VMTemplateVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Snapshot, SnapshotVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Network, NetworkVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.LoadBalancer, LoadBalancerVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.PortForwardingRule, PortForwardingRuleVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.FirewallRule, FirewallRuleVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.SecurityGroup, SecurityGroupVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.SecurityGroupRule, SecurityGroupRuleVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.PublicIpAddress, IPAddressVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Project, ProjectVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Account, AccountVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Vpc, VpcVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Nic, NicVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.NetworkACL, NetworkACLItemVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.StaticRoute, StaticRouteVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.VMSnapshot, VMSnapshotVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.RemoteAccessVpn, RemoteAccessVpnVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Zone, DataCenterVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.ServiceOffering, ServiceOfferingVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Storage, StoragePoolVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.PrivateGateway, RemoteAccessVpnVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.NetworkACLList, NetworkACLVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.VpnGateway, Site2SiteVpnGatewayVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.CustomerGateway, Site2SiteCustomerGatewayVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.VpnConnection, Site2SiteVpnConnectionVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.User, UserVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.DiskOffering, DiskOfferingVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.AutoScaleVmProfile, AutoScaleVmProfileVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.AutoScaleVmGroup, AutoScaleVmGroupVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.LBStickinessPolicy, LBStickinessPolicyVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.LBHealthCheckPolicy, LBHealthCheckPolicyVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.SnapshotPolicy, SnapshotPolicyVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.NetworkOffering, NetworkOfferingVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.VpcOffering, VpcOfferingVO.class); + s_typeMap.put(ResourceTag.ResourceObjectType.Domain, DomainVO.class); + } + + @Inject + EntityManager entityMgr; + @Inject + AccountManager accountMgr; + @Inject + DomainManager domainMgr; + + @Override + public long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType) { + Class clazz = s_typeMap.get(resourceType); + Object entity = entityMgr.findByUuid(clazz, resourceId); + if (entity != null) { + return ((InternalIdentity)entity).getId(); + } + if (!StringUtils.isNumeric(resourceId)) { + throw new InvalidParameterValueException("Unable to find resource by uuid " + resourceId + " and type " + resourceType); + } + entity = entityMgr.findById(clazz, resourceId); + if (entity != null) { + return ((InternalIdentity)entity).getId(); + } + throw new InvalidParameterValueException("Unable to find resource by id " + resourceId + " and type " + resourceType); + } + + @Override + public String getUuid(String resourceId, ResourceTag.ResourceObjectType resourceType) { + if (!StringUtils.isNumeric(resourceId)) { + return resourceId; + } + + Class clazz = s_typeMap.get(resourceType); + + Object entity = entityMgr.findById(clazz, resourceId); + if (entity != null && entity instanceof Identity) { + return ((Identity)entity).getUuid(); + } + + return resourceId; + } + + @Override + public ResourceTag.ResourceObjectType getResourceType(String resourceTypeStr) { + + for (ResourceTag.ResourceObjectType type : ResourceTag.ResourceObjectType.values()) { + if (type.toString().equalsIgnoreCase(resourceTypeStr)) { + return type; + } + } + throw new InvalidParameterValueException("Invalid resource type: " + resourceTypeStr); + } + + public void checkResourceAccessible(Long accountId, Long domainId, String exceptionMessage) { + Account caller = CallContext.current().getCallingAccount(); + if (Objects.equals(domainId, -1)) + { + throw new CloudRuntimeException("Invalid DomainId: -1"); + } + if (accountId != null) { + accountMgr.checkAccess(caller, null, false, accountMgr.getAccount(accountId)); + } else if (domainId != null && !accountMgr.isNormalUser(caller.getId())) { + //check permissions; + accountMgr.checkAccess(caller, domainMgr.getDomain(domainId)); + } else { + throw new PermissionDeniedException(exceptionMessage); + } + } +} diff --git a/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java b/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java index 8364185662a..db6cac0e393 100644 --- a/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java @@ -17,60 +17,33 @@ package com.cloud.tags; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; -import org.apache.cloudstack.api.Identity; -import org.apache.cloudstack.api.InternalIdentity; +import com.cloud.server.ResourceManagerUtil; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; -import com.cloud.dc.DataCenterVO; import com.cloud.domain.PartOf; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.PermissionDeniedException; -import com.cloud.network.LBHealthCheckPolicyVO; -import com.cloud.network.as.AutoScaleVmGroupVO; -import com.cloud.network.as.AutoScaleVmProfileVO; -import com.cloud.network.dao.IPAddressVO; -import com.cloud.network.dao.LBStickinessPolicyVO; -import com.cloud.network.dao.LoadBalancerVO; -import com.cloud.network.dao.NetworkVO; -import com.cloud.network.dao.RemoteAccessVpnVO; -import com.cloud.network.dao.Site2SiteCustomerGatewayVO; -import com.cloud.network.dao.Site2SiteVpnConnectionVO; -import com.cloud.network.dao.Site2SiteVpnGatewayVO; -import com.cloud.network.rules.FirewallRuleVO; -import com.cloud.network.rules.PortForwardingRuleVO; import com.cloud.network.security.SecurityGroupRuleVO; import com.cloud.network.security.SecurityGroupVO; import com.cloud.network.vpc.NetworkACLItemVO; import com.cloud.network.vpc.NetworkACLVO; -import com.cloud.network.vpc.StaticRouteVO; -import com.cloud.network.vpc.VpcOfferingVO; import com.cloud.network.vpc.VpcVO; -import com.cloud.offerings.NetworkOfferingVO; import com.cloud.projects.ProjectVO; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.TaggedResourceService; -import com.cloud.service.ServiceOfferingVO; -import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.SnapshotPolicyVO; -import com.cloud.storage.SnapshotVO; -import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VolumeVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.user.Account; @@ -78,7 +51,6 @@ import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; import com.cloud.user.DomainManager; import com.cloud.user.OwnedBy; -import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; @@ -90,54 +62,10 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.vm.NicVO; -import com.cloud.vm.UserVmVO; -import com.cloud.vm.snapshot.VMSnapshotVO; public class TaggedResourceManagerImpl extends ManagerBase implements TaggedResourceService { public static final Logger s_logger = Logger.getLogger(TaggedResourceManagerImpl.class); - private static final Map> s_typeMap = new HashMap<>(); - static { - s_typeMap.put(ResourceObjectType.UserVm, UserVmVO.class); - s_typeMap.put(ResourceObjectType.Volume, VolumeVO.class); - s_typeMap.put(ResourceObjectType.Template, VMTemplateVO.class); - s_typeMap.put(ResourceObjectType.ISO, VMTemplateVO.class); - s_typeMap.put(ResourceObjectType.Snapshot, SnapshotVO.class); - s_typeMap.put(ResourceObjectType.Network, NetworkVO.class); - s_typeMap.put(ResourceObjectType.LoadBalancer, LoadBalancerVO.class); - s_typeMap.put(ResourceObjectType.PortForwardingRule, PortForwardingRuleVO.class); - s_typeMap.put(ResourceObjectType.FirewallRule, FirewallRuleVO.class); - s_typeMap.put(ResourceObjectType.SecurityGroup, SecurityGroupVO.class); - s_typeMap.put(ResourceObjectType.SecurityGroupRule, SecurityGroupRuleVO.class); - s_typeMap.put(ResourceObjectType.PublicIpAddress, IPAddressVO.class); - s_typeMap.put(ResourceObjectType.Project, ProjectVO.class); - s_typeMap.put(ResourceObjectType.Account, AccountVO.class); - s_typeMap.put(ResourceObjectType.Vpc, VpcVO.class); - s_typeMap.put(ResourceObjectType.Nic, NicVO.class); - s_typeMap.put(ResourceObjectType.NetworkACL, NetworkACLItemVO.class); - s_typeMap.put(ResourceObjectType.StaticRoute, StaticRouteVO.class); - s_typeMap.put(ResourceObjectType.VMSnapshot, VMSnapshotVO.class); - s_typeMap.put(ResourceObjectType.RemoteAccessVpn, RemoteAccessVpnVO.class); - s_typeMap.put(ResourceObjectType.Zone, DataCenterVO.class); - s_typeMap.put(ResourceObjectType.ServiceOffering, ServiceOfferingVO.class); - s_typeMap.put(ResourceObjectType.Storage, StoragePoolVO.class); - s_typeMap.put(ResourceObjectType.PrivateGateway, RemoteAccessVpnVO.class); - s_typeMap.put(ResourceObjectType.NetworkACLList, NetworkACLVO.class); - s_typeMap.put(ResourceObjectType.VpnGateway, Site2SiteVpnGatewayVO.class); - s_typeMap.put(ResourceObjectType.CustomerGateway, Site2SiteCustomerGatewayVO.class); - s_typeMap.put(ResourceObjectType.VpnConnection, Site2SiteVpnConnectionVO.class); - s_typeMap.put(ResourceObjectType.User, UserVO.class); - s_typeMap.put(ResourceObjectType.DiskOffering, DiskOfferingVO.class); - s_typeMap.put(ResourceObjectType.AutoScaleVmProfile, AutoScaleVmProfileVO.class); - s_typeMap.put(ResourceObjectType.AutoScaleVmGroup, AutoScaleVmGroupVO.class); - s_typeMap.put(ResourceObjectType.LBStickinessPolicy, LBStickinessPolicyVO.class); - s_typeMap.put(ResourceObjectType.LBHealthCheckPolicy, LBHealthCheckPolicyVO.class); - s_typeMap.put(ResourceObjectType.SnapshotPolicy, SnapshotPolicyVO.class); - s_typeMap.put(ResourceObjectType.NetworkOffering, NetworkOfferingVO.class); - s_typeMap.put(ResourceObjectType.VpcOffering, VpcOfferingVO.class); - } - @Inject EntityManager _entityMgr; @Inject @@ -148,6 +76,8 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso DomainManager _domainMgr; @Inject AccountDao _accountDao; + @Inject + ResourceManagerUtil resourceManagerUtil; @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -164,25 +94,8 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso return true; } - @Override - public long getResourceId(String resourceId, ResourceObjectType resourceType) { - Class clazz = s_typeMap.get(resourceType); - Object entity = _entityMgr.findByUuid(clazz, resourceId); - if (entity != null) { - return ((InternalIdentity)entity).getId(); - } - if (!StringUtils.isNumeric(resourceId)) { - throw new InvalidParameterValueException("Unable to find resource by uuid " + resourceId + " and type " + resourceType); - } - entity = _entityMgr.findById(clazz, resourceId); - if (entity != null) { - return ((InternalIdentity)entity).getId(); - } - throw new InvalidParameterValueException("Unable to find resource by id " + resourceId + " and type " + resourceType); - } - private Pair getAccountDomain(long resourceId, ResourceObjectType resourceType) { - Class clazz = s_typeMap.get(resourceType); + Class clazz = ResourceManagerUtilImpl.s_typeMap.get(resourceType); Object entity = _entityMgr.findById(clazz, resourceId); Long accountId = null; @@ -191,7 +104,7 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso // if the resource type is a security group rule, get the accountId and domainId from the security group itself if (resourceType == ResourceObjectType.SecurityGroupRule) { SecurityGroupRuleVO rule = (SecurityGroupRuleVO)entity; - Object SecurityGroup = _entityMgr.findById(s_typeMap.get(ResourceObjectType.SecurityGroup), rule.getSecurityGroupId()); + Object SecurityGroup = _entityMgr.findById(ResourceManagerUtilImpl.s_typeMap.get(ResourceObjectType.SecurityGroup), rule.getSecurityGroupId()); accountId = ((SecurityGroupVO)SecurityGroup).getAccountId(); domainId = ((SecurityGroupVO)SecurityGroup).getDomainId(); @@ -206,11 +119,11 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso // if the resource type is network acl, get the accountId and domainId from VPC following: NetworkACLItem -> NetworkACL -> VPC if (resourceType == ResourceObjectType.NetworkACL) { NetworkACLItemVO aclItem = (NetworkACLItemVO)entity; - Object networkACL = _entityMgr.findById(s_typeMap.get(ResourceObjectType.NetworkACLList), aclItem.getAclId()); + Object networkACL = _entityMgr.findById(ResourceManagerUtilImpl.s_typeMap.get(ResourceObjectType.NetworkACLList), aclItem.getAclId()); Long vpcId = ((NetworkACLVO)networkACL).getVpcId(); if (vpcId != null && vpcId != 0) { - Object vpc = _entityMgr.findById(s_typeMap.get(ResourceObjectType.Vpc), vpcId); + Object vpc = _entityMgr.findById(ResourceManagerUtilImpl.s_typeMap.get(ResourceObjectType.Vpc), vpcId); accountId = ((VpcVO)vpc).getAccountId(); domainId = ((VpcVO)vpc).getDomainId(); @@ -243,49 +156,6 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso return new Pair<>(accountId, domainId); } - private void checkResourceAccessible(Long accountId, Long domainId, String exceptionMessage) { - Account caller = CallContext.current().getCallingAccount(); - if (Objects.equals(domainId, -1)) - { - throw new CloudRuntimeException("Invalid DomainId: -1"); - } - if (accountId != null) { - _accountMgr.checkAccess(caller, null, false, _accountMgr.getAccount(accountId)); - } else if (domainId != null && !_accountMgr.isNormalUser(caller.getId())) { - //check permissions; - _accountMgr.checkAccess(caller, _domainMgr.getDomain(domainId)); - } else { - throw new PermissionDeniedException(exceptionMessage); - } - } - - @Override - public ResourceObjectType getResourceType(String resourceTypeStr) { - - for (ResourceObjectType type : ResourceTag.ResourceObjectType.values()) { - if (type.toString().equalsIgnoreCase(resourceTypeStr)) { - return type; - } - } - throw new InvalidParameterValueException("Invalid resource type " + resourceTypeStr); - } - - @Override - public String getUuid(String resourceId, ResourceObjectType resourceType) { - if (!StringUtils.isNumeric(resourceId)) { - return resourceId; - } - - Class clazz = s_typeMap.get(resourceType); - - Object entity = _entityMgr.findById(clazz, resourceId); - if (entity != null && entity instanceof Identity) { - return ((Identity)entity).getUuid(); - } - - return resourceId; - } - @Override @DB @ActionEvent(eventType = EventTypes.EVENT_TAGS_CREATE, eventDescription = "creating resource tags") @@ -303,14 +173,14 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso throw new InvalidParameterValueException("The resource type " + resourceType + " doesn't support resource tags"); } - long id = getResourceId(resourceId, resourceType); - String resourceUuid = getUuid(resourceId, resourceType); + long id = resourceManagerUtil.getResourceId(resourceId, resourceType); + String resourceUuid = resourceManagerUtil.getUuid(resourceId, resourceType); Pair accountDomainPair = getAccountDomain(id, resourceType); Long domainId = accountDomainPair.second(); Long accountId = accountDomainPair.first(); - checkResourceAccessible(accountId, domainId, "Account '" + caller + + resourceManagerUtil.checkResourceAccessible(accountId, domainId, "Account '" + caller + "' doesn't have permissions to create tags" + " for resource '" + id + "(" + key + ")'."); String value = tags.get(key); @@ -335,7 +205,7 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso } private List searchResourceTags(List resourceIds, ResourceObjectType resourceType) { - List resourceUuids = resourceIds.stream().map(resourceId -> getUuid(resourceId, resourceType)).collect(Collectors.toList()); + List resourceUuids = resourceIds.stream().map(resourceId -> resourceManagerUtil.getUuid(resourceId, resourceType)).collect(Collectors.toList()); SearchBuilder sb = _resourceTagDao.createSearchBuilder(); sb.and("resourceUuid", sb.entity().getResourceUuid(), SearchCriteria.Op.IN); sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ); diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index 207270dd79c..d79908ecedf 100644 --- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -323,4 +323,10 @@ + + + + + diff --git a/server/src/test/java/com/cloud/metadata/ResourceMetaDataManagerTest.java b/server/src/test/java/com/cloud/metadata/ResourceMetaDataManagerTest.java index d6d8953d3fb..ff5ebb3deec 100644 --- a/server/src/test/java/com/cloud/metadata/ResourceMetaDataManagerTest.java +++ b/server/src/test/java/com/cloud/metadata/ResourceMetaDataManagerTest.java @@ -27,6 +27,7 @@ import java.util.Map; import javax.naming.ConfigurationException; +import com.cloud.server.ResourceManagerUtil; import org.apache.commons.collections.map.HashedMap; import org.junit.Before; import org.mockito.Mock; @@ -49,6 +50,8 @@ public class ResourceMetaDataManagerTest { NicDetailsDao _nicDetailDao; @Mock TaggedResourceService _taggedResourceMgr; + @Mock + ResourceManagerUtil resourceManagerUtil; @Before public void setup() { @@ -70,7 +73,7 @@ public class ResourceMetaDataManagerTest { public void testResourceDetails() throws ResourceAllocationException { //when(_resourceMetaDataMgr.getResourceId(anyString(), eq(ResourceTag.TaggedResourceType.Volume))).thenReturn(1L); - doReturn(1L).when(_taggedResourceMgr).getResourceId(anyString(), eq(ResourceTag.ResourceObjectType.Volume)); + doReturn(1L).when(resourceManagerUtil).getResourceId(anyString(), eq(ResourceTag.ResourceObjectType.Volume)); // _volumeDetailDao.removeDetails(id, key); doNothing().when(_volumeDetailDao).removeDetail(anyLong(), anyString()); @@ -82,7 +85,7 @@ public class ResourceMetaDataManagerTest { // Test adding details public void testAddResourceDetails() throws ResourceAllocationException { - doReturn(1L).when(_taggedResourceMgr).getResourceId("1", ResourceTag.ResourceObjectType.Volume); + doReturn(1L).when(resourceManagerUtil).getResourceId("1", ResourceTag.ResourceObjectType.Volume); // _volumeDetailDao.removeDetails(id, key); doNothing().when(_volumeDetailDao).removeDetail(anyLong(), anyString()); diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index 7ed3e98bd54..18996028182 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -136,6 +136,7 @@ known_categories = { 'Simulator': 'simulator', 'StaticRoute': 'VPC', 'Tags': 'Resource tags', + 'Icon': 'Resource Icon', 'NiciraNvpDevice': 'Nicira NVP', 'BrocadeVcsDevice': 'Brocade VCS', 'BigSwitchBcfDevice': 'BigSwitch BCF', diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index a2e8cb3fb45..0679a9bfb57 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -556,6 +556,7 @@ "label.character": "Character", "label.chassis": "Chassis", "label.checksum": "Checksum", +"label.choose.resource.icon": "Choose Icon", "label.choose.saml.indentity": "Choose SAML identity provider", "label.cidr": "CIDR", "label.cidr.account": "CIDR or Account/Security Group", @@ -718,6 +719,7 @@ "label.delete.events": "Delete events", "label.delete.f5": "Delete F5", "label.delete.gateway": "Delete gateway", +"label.delete.icon": "Delete icon", "label.delete.instance.group": "Delete Instance Group", "label.delete.internal.lb": "Delete Internal LB", "label.delete.netscaler": "Delete NetScaler", @@ -2249,7 +2251,9 @@ "label.upgrade.router.newer.template": "Upgrade Router to Use Newer Template", "label.upload": "Upload", "label.upload.from.local": "Upload from Local", +"label.upload.icon": "Upload Icon", "label.upload.iso.from.local": "Upload ISO from Local", +"label.upload.resource.icon": "Upload Icon", "label.upload.template.from.local": "Upload Template from Local", "label.upload.volume": "Upload volume", "label.upload.volume.from.local": "Upload Volume from Local", @@ -3248,6 +3252,7 @@ "message.success.delete": "Delete success", "message.success.delete.acl.rule": "Successfully removed ACL rule", "message.success.delete.backup.schedule": "Successfully deleted Configure VM backup schedule", +"message.success.delete.icon": "Successfully deleted icon of", "message.success.delete.snapshot.policy": "Successfully deleted snapshot policy", "message.success.delete.static.route": "Successfully deleted static route", "message.success.delete.tag": "Successfully deleted tag", @@ -3285,6 +3290,7 @@ "message.success.upgrade.kubernetes": "Successfully upgraded Kubernetes cluster", "message.success.upload": "Upload Successfully", "message.success.upload.description": "This ISO file has been uploaded. Please check its status at Templates menu", +"message.success.upload.icon": "Successfully uploaded icon for ", "message.success.upload.iso.description": "This ISO file has been uploaded. Please check its status in the Images > ISOs menu", "message.success.upload.template.description": "This template file has been uploaded. Please check its status at Templates menu", "message.success.upload.volume.description": "This Volume has been uploaded. Please check its status in the Volumes menu", @@ -3386,6 +3392,7 @@ "message.volume.state.uploadinprogress": "Volume upload is in progress", "message.volume.state.uploadop": "The volume upload operation is in progress or in short the volume is on secondary storage", "message.waiting.for.builtin.templates.to.load": "Waiting for builtin templates to load...", +"message.warn.filetype": "jpg, jpeg, png, bmp and svg are the only supported image formats", "message.xstools61plus.update.failed": "Failed to update Original XS Version is 6.1+ field. Error:", "message.you.must.have.at.least.one.physical.network": "You must have at least one physical network", "message.your.cloudstack.is.ready": "Your CloudStack is ready!", diff --git a/ui/src/components/header/ProjectMenu.vue b/ui/src/components/header/ProjectMenu.vue index dd5e614b4d9..3752bb02b99 100644 --- a/ui/src/components/header/ProjectMenu.vue +++ b/ui/src/components/header/ProjectMenu.vue @@ -39,6 +39,8 @@ + + {{ project.displaytext || project.name }} @@ -49,9 +51,13 @@ import store from '@/store' import { api } from '@/api' import _ from 'lodash' +import ResourceIcon from '@/components/view/ResourceIcon' export default { name: 'ProjectMenu', + components: { + ResourceIcon + }, data () { return { projects: [], @@ -70,7 +76,7 @@ export default { const projects = [] const getNextPage = () => { this.loading = true - api('listProjects', { listAll: true, details: 'min', page: page, pageSize: 500 }).then(json => { + api('listProjects', { listAll: true, details: 'min', page: page, pageSize: 500, showIcon: true }).then(json => { if (json && json.listprojectsresponse && json.listprojectsresponse.project) { projects.push(...json.listprojectsresponse.project) } diff --git a/ui/src/components/header/UserMenu.vue b/ui/src/components/header/UserMenu.vue index 33f4bc33b00..782fab45c56 100644 --- a/ui/src/components/header/UserMenu.vue +++ b/ui/src/components/header/UserMenu.vue @@ -26,7 +26,10 @@ - + + + + {{ nickname() }} @@ -64,16 +67,36 @@ diff --git a/ui/src/components/view/SearchView.vue b/ui/src/components/view/SearchView.vue index 86ffe88cdca..ad6a48b3abf 100644 --- a/ui/src/components/view/SearchView.vue +++ b/ui/src/components/view/SearchView.vue @@ -67,7 +67,21 @@ {{ $t(opt.name) }} + :value="opt.id"> + + + + + + + + + + + + + {{ $t(opt.name) }} + import { api } from '@/api' import TooltipButton from '@/components/widgets/TooltipButton' +import ResourceIcon from '@/components/view/ResourceIcon' export default { name: 'SearchView', components: { - TooltipButton + TooltipButton, + ResourceIcon }, props: { searchFilters: { @@ -341,7 +357,7 @@ export default { }, fetchZones () { return new Promise((resolve, reject) => { - api('listZones', { listAll: true }).then(json => { + api('listZones', { listAll: true, showicon: true }).then(json => { const zones = json.listzonesresponse.zone resolve({ type: 'zoneid', @@ -354,7 +370,7 @@ export default { }, fetchDomains () { return new Promise((resolve, reject) => { - api('listDomains', { listAll: true }).then(json => { + api('listDomains', { listAll: true, showicon: true }).then(json => { const domain = json.listdomainsresponse.domain resolve({ type: 'domainid', diff --git a/ui/src/components/view/TreeView.vue b/ui/src/components/view/TreeView.vue index feb0ad6392b..9bf1908ce50 100644 --- a/ui/src/components/view/TreeView.vue +++ b/ui/src/components/view/TreeView.vue @@ -82,6 +82,7 @@ import { api } from '@/api' import DetailsTab from '@/components/view/DetailsTab' import ResourceView from '@/components/view/ResourceView' import ResourceLayout from '@/layouts/ResourceLayout' +import eventBus from '@/config/eventBus' export default { name: 'TreeView', @@ -153,6 +154,9 @@ export default { this.metaName = this.$route.meta.name this.apiList = this.$route.meta.permission[0] ? this.$route.meta.permission[0] : '' this.apiChildren = this.$route.meta.permission[1] ? this.$route.meta.permission[1] : '' + eventBus.$on('refresh-domain-icon', () => { + this.getDetailResource(this.selectedTreeKey) + }) }, watch: { loading () { @@ -225,7 +229,8 @@ export default { const params = { listAll: true, - id: treeNode.eventKey + id: treeNode.eventKey, + showicon: true } return new Promise(resolve => { @@ -341,7 +346,6 @@ export default { this.treeViewData = [] this.loadingSearch = true this.$emit('change-tree-store', {}) - api(this.apiList, params).then(json => { const listDomains = this.getResponseJsonData(json) this.treeVerticalData = this.treeVerticalData.concat(listDomains) @@ -393,11 +397,11 @@ export default { // set id to parameter params.id = selectedKey params.listAll = true + params.showicon = true params.page = 1 params.pageSize = 1 this.detailLoading = true - api(apiName, params).then(json => { const jsonResponse = this.getResponseJsonData(json) diff --git a/ui/src/components/view/UploadResourceIcon.vue b/ui/src/components/view/UploadResourceIcon.vue new file mode 100644 index 00000000000..ecf6d6668e0 --- /dev/null +++ b/ui/src/components/view/UploadResourceIcon.vue @@ -0,0 +1,314 @@ +// 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. + + + + diff --git a/ui/src/core/lazy_lib/components_use.js b/ui/src/core/lazy_lib/components_use.js index 50e2d96d0d8..a9adc5d924d 100644 --- a/ui/src/core/lazy_lib/components_use.js +++ b/ui/src/core/lazy_lib/components_use.js @@ -68,6 +68,7 @@ import { AutoComplete, Collapse } from 'ant-design-vue' +import VueCropper from 'vue-cropper' Vue.use(ConfigProvider) Vue.use(Layout) @@ -117,6 +118,7 @@ Vue.use(Calendar) Vue.use(Slider) Vue.use(AutoComplete) Vue.use(Collapse) +Vue.use(VueCropper) Vue.prototype.$confirm = Modal.confirm Vue.prototype.$message = message diff --git a/ui/src/main.js b/ui/src/main.js index adc202240ed..0635b99765e 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -26,7 +26,7 @@ import './core/lazy_use' import './core/ext' import './permission' // permission control import './utils/filter' // global filter -import { pollJobPlugin, notifierPlugin, toLocaleDatePlugin, configUtilPlugin, apiMetaUtilPlugin } from './utils/plugins' +import { pollJobPlugin, notifierPlugin, toLocaleDatePlugin, configUtilPlugin, apiMetaUtilPlugin, showIconPlugin, resourceTypePlugin } from './utils/plugins' import { VueAxios } from './utils/request' import './utils/directives' @@ -35,6 +35,8 @@ Vue.use(VueAxios, router) Vue.use(pollJobPlugin) Vue.use(notifierPlugin) Vue.use(toLocaleDatePlugin) +Vue.use(showIconPlugin) +Vue.use(resourceTypePlugin) fetch('config.json').then(response => response.json()).then(config => { Vue.prototype.$config = config diff --git a/ui/src/utils/plugins.js b/ui/src/utils/plugins.js index 8d196157fa0..99db8f7eb6e 100644 --- a/ui/src/utils/plugins.js +++ b/ui/src/utils/plugins.js @@ -250,6 +250,39 @@ export const configUtilPlugin = { } } +export const showIconPlugin = { + install (Vue) { + Vue.prototype.$showIcon = function (resource) { + var resourceType = this.$route.path.split('/')[1] + if (resource) { + resourceType = resource + } + if (['zone', 'template', 'iso', 'account', 'accountuser', 'vm', 'domain', 'project', 'vpc', 'guestnetwork'].includes(resourceType)) { + return true + } else { + return false + } + } + } +} + +export const resourceTypePlugin = { + install (Vue) { + Vue.prototype.$getResourceType = function () { + const type = this.$route.path.split('/')[1] + if (type === 'vm') { + return 'UserVM' + } else if (type === 'accountuser') { + return 'User' + } else if (type === 'guestnetwork') { + return 'Network' + } else { + return type + } + } + } +} + export const apiMetaUtilPlugin = { install (Vue) { Vue.prototype.$getApiParams = function () { diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index 31fc4da8fdf..c924486eff3 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -271,6 +271,44 @@ > {{ }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ opt.name || opt.description || opt.traffictype || opt.publicip }} @@ -411,6 +449,8 @@ import ListView from '@/components/view/ListView' import ResourceView from '@/components/view/ResourceView' import ActionButton from '@/components/view/ActionButton' import SearchView from '@/components/view/SearchView' +import OsLogo from '@/components/widgets/OsLogo' +import ResourceIcon from '@/components/view/ResourceIcon' import BulkActionProgress from '@/components/view/BulkActionProgress' import TooltipLabel from '@/components/widgets/TooltipLabel' @@ -425,7 +465,9 @@ export default { ActionButton, SearchView, BulkActionProgress, - TooltipLabel + TooltipLabel, + OsLogo, + ResourceIcon }, mixins: [mixinDevice], provide: function () { @@ -493,6 +535,11 @@ export default { this.fetchData() } }) + eventBus.$on('refresh-icon', () => { + if (this.$showIcon()) { + this.fetchData() + } + }) eventBus.$on('async-job-complete', (action) => { if (this.$route.path.includes('/vm/')) { if (action && 'api' in action && ['destroyVirtualMachine'].includes(action.api)) { @@ -785,7 +832,11 @@ export default { params.page = this.page params.pagesize = this.pageSize + this.searchParams = params + if (this.$showIcon()) { + params.showIcon = true + } api(this.apiName, params).then(json => { var responseName var objectName @@ -986,6 +1037,10 @@ export default { var extractedParamName = paramName.replace('ids', '').replace('id', '').toLowerCase() var params = { listall: true } const possibleName = 'list' + extractedParamName + 's' + var showIcon = false + if (this.$showIcon(extractedParamName)) { + showIcon = true + } var possibleApi if (this.currentAction.mapping && param.name in this.currentAction.mapping && this.currentAction.mapping[param.name].api) { possibleApi = this.currentAction.mapping[param.name].api @@ -1017,6 +1072,9 @@ export default { } else if (possibleApi === 'listHosts') { params.type = 'routing' } + if (showIcon) { + params.showicon = true + } api(possibleApi, params).then(json => { param.loading = false for (const obj in json) { diff --git a/ui/src/views/compute/AssignInstance.vue b/ui/src/views/compute/AssignInstance.vue index 085c53fae79..a4869742046 100644 --- a/ui/src/views/compute/AssignInstance.vue +++ b/ui/src/views/compute/AssignInstance.vue @@ -55,6 +55,8 @@ return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0 }" > + + {{ domain.path || domain.name || domain.description }} @@ -72,6 +74,8 @@ return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0 }" > + + {{ account.name }} @@ -91,6 +95,8 @@ return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0 }" > + + {{ project.name }} @@ -108,6 +114,8 @@ return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0 }" > + + {{ network.name ? network.name : '-' }} @@ -129,6 +137,7 @@