From 366b253bf2cdc069059fb9953587ec5129db31a3 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 12 Apr 2019 12:24:23 +0530 Subject: [PATCH] server: create, update VPC offering for domain(s) & zone(s) Signed-off-by: Abhishek Kumar --- .../com/cloud/network/vpc/VpcOffering.java | 11 +- .../network/vpc/VpcProvisioningService.java | 22 +- .../admin/vpc/CreateVPCOfferingCmd.java | 41 +++- .../admin/vpc/UpdateVPCOfferingCmd.java | 42 +++- .../api/response/VpcOfferingResponse.java | 51 ++++- .../network/vpc/VpcOfferingDetailsVO.java | 87 ++++++++ .../com/cloud/network/vpc/VpcOfferingVO.java | 16 +- .../vpc/dao/VpcOfferingDetailsDao.java | 30 +++ .../vpc/dao/VpcOfferingDetailsDaoImpl.java | 58 +++++ ...spring-engine-schema-core-daos-context.xml | 2 + .../META-INF/db/schema-41200to41300.sql | 51 ++++- .../management/ContrailManagerImpl.java | 2 +- .../main/java/com/cloud/api/ApiDBUtils.java | 15 ++ .../java/com/cloud/api/ApiResponseHelper.java | 15 +- .../api/query/dao/VpcOfferingJoinDao.java | 35 +++ .../api/query/dao/VpcOfferingJoinDaoImpl.java | 87 ++++++++ .../cloud/api/query/vo/VpcOfferingJoinVO.java | 198 +++++++++++++++++ .../com/cloud/network/vpc/VpcManagerImpl.java | 204 +++++++++++++++--- 18 files changed, 907 insertions(+), 60 deletions(-) create mode 100644 engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingDetailsVO.java create mode 100644 engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDetailsDao.java create mode 100644 engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDetailsDaoImpl.java create mode 100644 server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDao.java create mode 100644 server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java create mode 100644 server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java diff --git a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java index c0da6bf967a..b4df8e38dba 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.network.vpc; +import java.util.Date; + import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -59,13 +61,16 @@ public interface VpcOffering extends InternalIdentity, Identity { /** * @return true if the offering provides a distributed router capable of one-hop forwarding */ - boolean supportsDistributedRouter(); + boolean isSupportsDistributedRouter(); /** * @return true if VPC created with the offering can span multiple zones in the region */ - boolean offersRegionLevelVPC(); + boolean isOffersRegionLevelVPC(); - boolean getRedundantRouter(); + boolean isRedundantRouter(); + Date getRemoved(); + + Date getCreated(); } diff --git a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java index cce285030b9..5edf68ae194 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java @@ -20,16 +20,21 @@ package com.cloud.network.vpc; import java.util.List; import java.util.Map; +import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd; +import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd; + import com.cloud.utils.Pair; public interface VpcProvisioningService { - public VpcOffering getVpcOffering(long vpcOfferingId); + VpcOffering getVpcOffering(long vpcOfferingId); - public VpcOffering createVpcOffering(String name, String displayText, List supportedServices, - Map> serviceProviders, - Map serviceCapabilitystList, - Long serviceOfferingId); + VpcOffering createVpcOffering(CreateVPCOfferingCmd cmd); + + VpcOffering createVpcOffering(String name, String displayText, List supportedServices, + Map> serviceProviders, + Map serviceCapabilitystList, + Long serviceOfferingId, List domainIds, List zoneIds); Pair,Integer> listVpcOfferings(Long id, String name, String displayText, List supportedServicesStr, Boolean isDefault, String keyword, String state, Long startIndex, Long pageSizeVal); @@ -41,12 +46,9 @@ public interface VpcProvisioningService { public boolean deleteVpcOffering(long offId); /** - * @param vpcOffId - * @param vpcOfferingName - * @param displayText - * @param state + * @param cmd * @return */ - public VpcOffering updateVpcOffering(long vpcOffId, String vpcOfferingName, String displayText, String state); + public VpcOffering updateVpcOffering(UpdateVPCOfferingCmd cmd); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java index 99c1719e1b6..d629a6e789c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java @@ -20,9 +20,14 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -75,6 +80,21 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { description = "the ID of the service offering for the VPC router appliance") private Long serviceOfferingId; + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = DomainResponse.class, + description = "the ID of the containing domain(s), null for public offerings") + private List domainIds; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = ZoneResponse.class, + description = "the ID of the containing zone(s), null for public offerings", + since = "4.13") + private List zoneIds; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -127,10 +147,27 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { return serviceOfferingId; } + public List getDomainIds() { + if (CollectionUtils.isNotEmpty(domainIds)) { + Set set = new LinkedHashSet<>(domainIds); + domainIds.clear(); + domainIds.addAll(set); + } + return domainIds; + } + + public List getZoneIds() { + if (CollectionUtils.isNotEmpty(zoneIds)) { + Set set = new LinkedHashSet<>(zoneIds); + zoneIds.clear(); + zoneIds.addAll(set); + } + return zoneIds; + } + @Override public void create() throws ResourceAllocationException { - VpcOffering vpcOff = _vpcProvSvc.createVpcOffering(getVpcOfferingName(), getDisplayText(), - getSupportedServices(), getServiceProviders(), getServiceCapabilitystList(), getServiceOfferingId()); + VpcOffering vpcOff = _vpcProvSvc.createVpcOffering(this); if (vpcOff != null) { setEntityId(vpcOff.getId()); setEntityUuid(vpcOff.getUuid()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/UpdateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/UpdateVPCOfferingCmd.java index 5af47d3a4c7..aeeb7ae2acb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/UpdateVPCOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/UpdateVPCOfferingCmd.java @@ -16,6 +16,13 @@ // under the License. package org.apache.cloudstack.api.command.admin.vpc; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -52,6 +59,21 @@ public class UpdateVPCOfferingCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "update state for the VPC offering; " + "supported states - Enabled/Disabled") private String state; + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = DomainResponse.class, + description = "the ID of the containing domain(s), null for public offerings") + private List domainIds; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = ZoneResponse.class, + description = "the ID of the containing zone(s), null for public offerings", + since = "4.13") + private List zoneIds; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -72,6 +94,24 @@ public class UpdateVPCOfferingCmd extends BaseAsyncCmd { return state; } + public List getDomainIds() { + if (CollectionUtils.isNotEmpty(domainIds)) { + Set set = new LinkedHashSet<>(domainIds); + domainIds.clear(); + domainIds.addAll(set); + } + return domainIds; + } + + public List getZoneIds() { + if (CollectionUtils.isNotEmpty(zoneIds)) { + Set set = new LinkedHashSet<>(zoneIds); + zoneIds.clear(); + zoneIds.addAll(set); + } + return zoneIds; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -87,7 +127,7 @@ public class UpdateVPCOfferingCmd extends BaseAsyncCmd { @Override public void execute() { - VpcOffering result = _vpcProvSvc.updateVpcOffering(getId(), getVpcOfferingName(), getDisplayText(), getState()); + VpcOffering result = _vpcProvSvc.updateVpcOffering(this); if (result != null) { VpcOfferingResponse response = _responseGenerator.createVpcOfferingResponse(result); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java index 512746fb6af..b8483c3e3d2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java @@ -19,14 +19,13 @@ package org.apache.cloudstack.api.response; import java.util.Date; import java.util.List; -import com.google.gson.annotations.SerializedName; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; import com.cloud.network.vpc.VpcOffering; import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; @EntityReference(value = VpcOffering.class) @SuppressWarnings("unused") @@ -67,6 +66,22 @@ public class VpcOfferingResponse extends BaseResponse { @Param(description = " indicated if the offering can support region level vpc", since = "4.4") private Boolean supportsRegionLevelVpc; + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") + private String domain; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "the zone ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + private String zoneId; + + @SerializedName(ApiConstants.ZONE) + @Param(description = "the zone name(s) this disk offering belongs to. Ignore this information as it is not currently applicable.", since = "4.13.0") + private String zone; + public void setId(String id) { this.id = id; } @@ -102,4 +117,36 @@ public class VpcOfferingResponse extends BaseResponse { public void setSupportsRegionLevelVpc(Boolean supports) { this.supportsRegionLevelVpc = supports; } + + public String getDomainId() { + return domainId; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public String getZone() { + return zone; + } + + public void setZone(String zone) { + this.zone = zone; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingDetailsVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingDetailsVO.java new file mode 100644 index 00000000000..3197ffc10ca --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingDetailsVO.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.network.vpc; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.ResourceDetail; + +@Entity +@Table(name = "vpc_offering_details") +public class VpcOfferingDetailsVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "offering_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value") + private String value; + + @Column(name = "display") + private boolean display = true; + + protected VpcOfferingDetailsVO() { + } + + public VpcOfferingDetailsVO(long vpcOfferingId, String name, String value, boolean display) { + this.resourceId = vpcOfferingId; + this.name = name; + this.value = value; + this.display = display; + } + + @Override + public long getResourceId() { + return resourceId; + } + + public void setResourceId(long vpcOfferingId) { + this.resourceId = vpcOfferingId; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public long getId() { + return id; + } + + @Override + public boolean isDisplay() { + return display; + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java index 62e8cf3ae2a..b91072d0102 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java @@ -163,23 +163,33 @@ public class VpcOfferingVO implements VpcOffering { this.state = state; } + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } + @Override public Long getServiceOfferingId() { return serviceOfferingId; } @Override - public boolean supportsDistributedRouter() { + public boolean isSupportsDistributedRouter() { return supportsDistributedRouter; } @Override - public boolean offersRegionLevelVPC() { + public boolean isOffersRegionLevelVPC() { return offersRegionLevelVPC; } @Override - public boolean getRedundantRouter() { + public boolean isRedundantRouter() { return this.redundantRouter; } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDetailsDao.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDetailsDao.java new file mode 100644 index 00000000000..da5bf52f764 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDetailsDao.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.network.vpc.dao; + +import java.util.List; + +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import com.cloud.network.vpc.VpcOfferingDetailsVO; +import com.cloud.utils.db.GenericDao; + +public interface VpcOfferingDetailsDao extends GenericDao, ResourceDetailsDao { + List findDomainIds(final long resourceId); + List findZoneIds(final long resourceId); +} diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDetailsDaoImpl.java new file mode 100644 index 00000000000..0feacd99160 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDetailsDaoImpl.java @@ -0,0 +1,58 @@ +// 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.network.vpc.dao; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; + +import com.cloud.network.vpc.VpcOfferingDetailsVO; + +public class VpcOfferingDetailsDaoImpl extends ResourceDetailsDaoBase implements VpcOfferingDetailsDao { + + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new VpcOfferingDetailsVO(resourceId, key, value, display)); + } + + @Override + public List findDomainIds(long resourceId) { + final List domainIds = new ArrayList<>(); + for (final VpcOfferingDetailsVO detail: findDetails(resourceId, ApiConstants.DOMAIN_ID)) { + final Long domainId = Long.valueOf(detail.getValue()); + if (domainId > 0) { + domainIds.add(domainId); + } + } + return domainIds; + } + + @Override + public List findZoneIds(long resourceId) { + final List zoneIds = new ArrayList<>(); + for (final VpcOfferingDetailsVO detail: findDetails(resourceId, ApiConstants.ZONE_ID)) { + final Long zoneId = Long.valueOf(detail.getValue()); + if (zoneId > 0) { + zoneIds.add(zoneId); + } + } + return zoneIds; + } +} \ No newline at end of file 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 8e30cc23c2c..49614eeec84 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 @@ -324,12 +324,14 @@ + + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41200to41300.sql b/engine/schema/src/main/resources/META-INF/db/schema-41200to41300.sql index a73675fedc0..e7d3cf2ae1b 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41200to41300.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41200to41300.sql @@ -218,4 +218,53 @@ CREATE VIEW `cloud`.`network_offering_view` AS LEFT JOIN `cloud`.`data_center` AS `zone` ON FIND_IN_SET(`zone`.`id`, `zone_details`.`value`) GROUP BY - `network_offerings`.`id`; \ No newline at end of file + `network_offerings`.`id`; + +-- Create VPC offering details table +CREATE TABLE `vpc_offering_details` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `offering_id` bigint(20) unsigned NOT NULL COMMENT 'vpc offering id', + `name` varchar(255) NOT NULL, + `value` varchar(1024) NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user', + PRIMARY KEY (`id`), + KEY `fk_vpc_offering_details__vpc_offering_id` (`offering_id`), + CONSTRAINT `fk_vpc_offering_details__vpc_offering_id` FOREIGN KEY (`offering_id`) REFERENCES `vpc_offerings` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- VPC offering with multi-domains and multi-zones +DROP VIEW IF EXISTS `cloud`.`vpc_offering_view`; +CREATE VIEW `cloud`.`vpc_offering_view` AS + SELECT + `vpc_offerings`.`id` AS `id`, + `vpc_offerings`.`uuid` AS `uuid`, + `vpc_offerings`.`name` AS `name`, + `vpc_offerings`.`unique_name` AS `unique_name`, + `vpc_offerings`.`display_text` AS `display_text`, + `vpc_offerings`.`state` AS `state`, + `vpc_offerings`.`default` AS `default`, + `vpc_offerings`.`created` AS `created`, + `vpc_offerings`.`removed` AS `removed`, + `vpc_offerings`.`service_offering_id` AS `service_offering_id`, + `vpc_offerings`.`supports_distributed_router` AS `supports_distributed_router`, + `vpc_offerings`.`supports_region_level_vpc` AS `supports_region_level_vpc`, + `vpc_offerings`.`redundant_router_service` AS `redundant_router_service`, + GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, + GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, + GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, + GROUP_CONCAT(DISTINCT(domain.path)) AS domain_path, + GROUP_CONCAT(DISTINCT(zone.id)) AS zone_id, + GROUP_CONCAT(DISTINCT(zone.uuid)) AS zone_uuid, + GROUP_CONCAT(DISTINCT(zone.name)) AS zone_name + FROM + `cloud`.`vpc_offerings` + LEFT JOIN + `cloud`.`vpc_offering_details` AS `domain_details` ON `domain_details`.`offering_id` = `vpc_offerings`.`id` AND `domain_details`.`name`='domainid' + LEFT JOIN + `cloud`.`domain` AS `domain` ON FIND_IN_SET(`domain`.`id`, `domain_details`.`value`) + LEFT JOIN + `cloud`.`vpc_offering_details` AS `zone_details` ON `zone_details`.`offering_id` = `vpc_offerings`.`id` AND `zone_details`.`name`='zoneid' + LEFT JOIN + `cloud`.`data_center` AS `zone` ON FIND_IN_SET(`zone`.`id`, `zone_details`.`value`) + GROUP BY + `vpc_offerings`.`id`; \ No newline at end of file diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java index 65b7f2719a6..aee24b05d57 100644 --- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java +++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java @@ -299,7 +299,7 @@ public class ContrailManagerImpl extends ManagerBase implements ContrailManager } serviceProviderMap.put(svc, providerSet); } - vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null); + vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null, null, null); ((VpcOfferingVO)vpcOffer).setState(VpcOffering.State.Enabled); long id = vpcOffer.getId(); _vpcOffDao.update(id, (VpcOfferingVO)vpcOffer); diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index 34e249d848e..844c0a5b02f 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -61,6 +61,7 @@ import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.api.response.VpcOfferingResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -97,6 +98,7 @@ import com.cloud.api.query.dao.TemplateJoinDao; import com.cloud.api.query.dao.UserAccountJoinDao; import com.cloud.api.query.dao.UserVmJoinDao; import com.cloud.api.query.dao.VolumeJoinDao; +import com.cloud.api.query.dao.VpcOfferingJoinDao; import com.cloud.api.query.vo.AccountJoinVO; import com.cloud.api.query.vo.AffinityGroupJoinVO; import com.cloud.api.query.vo.AsyncJobJoinVO; @@ -121,6 +123,7 @@ import com.cloud.api.query.vo.TemplateJoinVO; import com.cloud.api.query.vo.UserAccountJoinVO; import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.api.query.vo.VolumeJoinVO; +import com.cloud.api.query.vo.VpcOfferingJoinVO; import com.cloud.capacity.CapacityManager; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; @@ -425,6 +428,7 @@ public class ApiDBUtils { static VpcGatewayDao s_vpcGatewayDao; static VpcDao s_vpcDao; static VpcOfferingDao s_vpcOfferingDao; + static VpcOfferingJoinDao s_vpcOfferingJoinDao; static SnapshotPolicyDao s_snapshotPolicyDao; static AsyncJobDao s_asyncJobDao; static HostDetailsDao s_hostDetailsDao; @@ -644,6 +648,8 @@ public class ApiDBUtils { @Inject private VpcOfferingDao vpcOfferingDao; @Inject + private VpcOfferingJoinDao vpcOfferingJoinDao; + @Inject private SnapshotPolicyDao snapshotPolicyDao; @Inject private AsyncJobDao asyncJobDao; @@ -780,6 +786,7 @@ public class ApiDBUtils { s_asVmGroupDao = asVmGroupDao; s_vpcDao = vpcDao; s_vpcOfferingDao = vpcOfferingDao; + s_vpcOfferingJoinDao = vpcOfferingJoinDao; s_snapshotPolicyDao = snapshotPolicyDao; s_asyncJobDao = asyncJobDao; s_hostDetailsDao = hostDetailsDao; @@ -1542,6 +1549,14 @@ public class ApiDBUtils { return s_vpcOfferingDao.findById(offeringId); } + public static VpcOfferingResponse newVpcOfferingResponse(VpcOffering offering) { + return s_vpcOfferingJoinDao.newVpcOfferingResponse(offering); + } + + public static VpcOfferingJoinVO newVpcOfferingView(VpcOffering offering) { + return s_vpcOfferingJoinDao.newVpcOfferingView(offering); + } + public static NetworkACL findByNetworkACLId(long aclId) { return s_networkACLDao.findById(aclId); } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 2571596f10d..7e11ba08746 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -187,6 +187,7 @@ import com.cloud.api.query.vo.TemplateJoinVO; import com.cloud.api.query.vo.UserAccountJoinVO; import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.api.query.vo.VolumeJoinVO; +import com.cloud.api.query.vo.VpcOfferingJoinVO; import com.cloud.api.response.ApiResponseSerializer; import com.cloud.capacity.Capacity; import com.cloud.capacity.CapacityVO; @@ -2799,15 +2800,10 @@ public class ApiResponseHelper implements ResponseGenerator { @Override public VpcOfferingResponse createVpcOfferingResponse(VpcOffering offering) { - VpcOfferingResponse response = new VpcOfferingResponse(); - response.setId(offering.getUuid()); - response.setName(offering.getName()); - response.setDisplayText(offering.getDisplayText()); - response.setIsDefault(offering.isDefault()); - response.setState(offering.getState().name()); - response.setSupportsDistributedRouter(offering.supportsDistributedRouter()); - response.setSupportsRegionLevelVpc(offering.offersRegionLevelVPC()); - + if (!(offering instanceof VpcOfferingJoinVO)) { + offering = ApiDBUtils.newVpcOfferingView(offering); + } + VpcOfferingResponse response = ApiDBUtils.newVpcOfferingResponse(offering); Map> serviceProviderMap = ApiDBUtils.listVpcOffServices(offering.getId()); List serviceResponses = new ArrayList(); for (Map.Entry> entry : serviceProviderMap.entrySet()) { @@ -2833,7 +2829,6 @@ public class ApiResponseHelper implements ResponseGenerator { serviceResponses.add(svcRsp); } response.setServices(serviceResponses); - response.setObjectName("vpcoffering"); return response; } diff --git a/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDao.java new file mode 100644 index 00000000000..fe4c4edee38 --- /dev/null +++ b/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDao.java @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.api.query.dao; + +import java.util.List; + +import org.apache.cloudstack.api.response.VpcOfferingResponse; + +import com.cloud.api.query.vo.VpcOfferingJoinVO; +import com.cloud.network.vpc.VpcOffering; +import com.cloud.utils.db.GenericDao; + +public interface VpcOfferingJoinDao extends GenericDao { + + List findByDomainId(long domainId); + + VpcOfferingResponse newVpcOfferingResponse(VpcOffering offering); + + VpcOfferingJoinVO newVpcOfferingView(VpcOffering offering); +} diff --git a/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java new file mode 100644 index 00000000000..cc427b52333 --- /dev/null +++ b/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.api.query.dao; + +import java.util.List; + +import org.apache.cloudstack.api.response.VpcOfferingResponse; +import org.apache.log4j.Logger; + +import com.cloud.api.query.vo.VpcOfferingJoinVO; +import com.cloud.network.vpc.VpcOffering; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +public class VpcOfferingJoinDaoImpl extends GenericDaoBase implements VpcOfferingJoinDao { + public static final Logger s_logger = Logger.getLogger(VpcOfferingJoinDaoImpl.class); + + private SearchBuilder sofIdSearch; + + protected VpcOfferingJoinDaoImpl() { + + sofIdSearch = createSearchBuilder(); + sofIdSearch.and("id", sofIdSearch.entity().getId(), SearchCriteria.Op.EQ); + sofIdSearch.done(); + + this._count = "select count(distinct service_offering_view.id) from service_offering_view WHERE "; + } + + @Override + public List findByDomainId(long domainId) { + SearchBuilder sb = createSearchBuilder(); + sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.FIND_IN_SET); + sb.done(); + + SearchCriteria sc = sb.create(); + sc.setParameters("domainId", String.valueOf(domainId)); + return listBy(sc); + } + + @Override + public VpcOfferingResponse newVpcOfferingResponse(VpcOffering offering) { + VpcOfferingResponse offeringResponse = new VpcOfferingResponse(); + offeringResponse.setId(offering.getUuid()); + offeringResponse.setName(offering.getName()); + offeringResponse.setDisplayText(offering.getDisplayText()); + offeringResponse.setIsDefault(offering.isDefault()); + offeringResponse.setState(offering.getState().name()); + offeringResponse.setSupportsDistributedRouter(offering.isSupportsDistributedRouter()); + offeringResponse.setSupportsRegionLevelVpc(offering.isOffersRegionLevelVPC()); + offeringResponse.setCreated(offering.getCreated()); + if (offering instanceof VpcOfferingJoinVO) { + VpcOfferingJoinVO offeringJoinVO = (VpcOfferingJoinVO) offering; + offeringResponse.setDomainId(offeringJoinVO.getDomainUuid()); + offeringResponse.setDomain(offeringJoinVO.getDomainPath()); + offeringResponse.setZoneId(offeringJoinVO.getZoneUuid()); + offeringResponse.setZone(offeringJoinVO.getZoneName()); + } + offeringResponse.setObjectName("vpcoffering"); + + return offeringResponse; + } + + @Override + public VpcOfferingJoinVO newVpcOfferingView(VpcOffering offering) { + SearchCriteria sc = sofIdSearch.create(); + sc.setParameters("id", offering.getId()); + List offerings = searchIncludingRemoved(sc, null, null, false); + assert offerings != null && offerings.size() == 1 : "No VPC offering found for offering id " + offering.getId(); + return offerings.get(0); + } +} \ No newline at end of file diff --git a/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java new file mode 100644 index 00000000000..68ebdcf1eec --- /dev/null +++ b/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java @@ -0,0 +1,198 @@ +// 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.api.query.vo; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.network.vpc.VpcOffering; +import com.cloud.utils.db.GenericDao; + +@Entity +@Table(name = "vpc_offering_view") +public class VpcOfferingJoinVO implements VpcOffering { + + @Id + @Column(name = "id", updatable = false, nullable = false) + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + String name; + + @Column(name = "unique_name") + String uniqueName; + + @Column(name = "display_text") + String displayText; + + @Column(name = "state") + @Enumerated(value = EnumType.STRING) + VpcOffering.State state = VpcOffering.State.Disabled; + + @Column(name = "default") + boolean isDefault = false; + + @Column(name = GenericDao.REMOVED_COLUMN) + Date removed; + + @Column(name = GenericDao.CREATED_COLUMN) + Date created; + + @Column(name = "service_offering_id") + Long serviceOfferingId; + + @Column(name = "supports_distributed_router") + boolean supportsDistributedRouter=false; + + @Column(name = "supports_region_level_vpc") + boolean offersRegionLevelVPC = false; + + @Column(name = "redundant_router_service") + boolean redundantRouter = false; + + @Column(name = "domain_id") + private String domainId; + + @Column(name = "domain_uuid") + private String domainUuid; + + @Column(name = "domain_name") + private String domainName = null; + + @Column(name = "domain_path") + private String domainPath = null; + + @Column(name = "zone_id") + private String zoneId = null; + + @Column(name = "zone_uuid") + private String zoneUuid = null; + + @Column(name = "zone_name") + private String zoneName = null; + + public VpcOfferingJoinVO() { + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + public String getUniqueName() { + return uniqueName; + } + + @Override + public String getDisplayText() { + return displayText; + } + + @Override + public VpcOffering.State getState() { + return state; + } + + @Override + public boolean isDefault() { + return isDefault; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Long getServiceOfferingId() { + return serviceOfferingId; + } + + @Override + public boolean isSupportsDistributedRouter() { + return supportsDistributedRouter; + } + + @Override + public boolean isOffersRegionLevelVPC() { + return offersRegionLevelVPC; + } + + @Override + public boolean isRedundantRouter() { + return redundantRouter; + } + + public String getDomainId() { + return domainId; + } + + public String getDomainUuid() { + return domainUuid; + } + + public String getDomainName() { + return domainName; + } + + public String getDomainPath() { + return domainPath; + } + + public String getZoneId() { + return zoneId; + } + + public String getZoneUuid() { + return zoneUuid; + } + + public String getZoneName() { + return zoneName; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder("[VPC Offering ["); + return buf.append(id).append("-").append(name).append("]").toString(); + } +} diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java index a4ea50d94dd..09de4b30623 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -39,6 +39,9 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd; +import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd; import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd; import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd; import org.apache.cloudstack.context.CallContext; @@ -57,6 +60,7 @@ import com.cloud.dc.VlanVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DeployDestination; +import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; @@ -97,6 +101,7 @@ import com.cloud.network.vpc.dao.StaticRouteDao; import com.cloud.network.vpc.dao.VpcDao; import com.cloud.network.vpc.dao.VpcGatewayDao; import com.cloud.network.vpc.dao.VpcOfferingDao; +import com.cloud.network.vpc.dao.VpcOfferingDetailsDao; import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; import com.cloud.network.vpc.dao.VpcServiceMapDao; import com.cloud.network.vpn.Site2SiteVpnManager; @@ -153,6 +158,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Inject VpcOfferingDao _vpcOffDao; @Inject + VpcOfferingDetailsDao vpcOfferingDetailsDao; + @Inject VpcOfferingServiceMapDao _vpcOffSvcMapDao; @Inject VpcDao _vpcDao; @@ -204,6 +211,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis VpcVirtualNetworkApplianceManager _routerService; @Inject DomainRouterDao _routerDao; + @Inject + DomainDao domainDao; @Inject private VpcPrivateGatewayTransactionCallable vpcTxCallable; @@ -341,10 +350,47 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return _vpcOffDao.findById(vpcOffId); } + @Override + @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_CREATE, eventDescription = "creating vpc offering", create = true) + public VpcOffering createVpcOffering(CreateVPCOfferingCmd cmd) { + final String vpcOfferingName = cmd.getVpcOfferingName(); + final String displayText = cmd.getDisplayText(); + final List supportedServices = cmd.getSupportedServices(); + final Map> serviceProviderList = cmd.getServiceProviders(); + final Map> serviceCapabilitystList = cmd.getServiceCapabilitystList(); + final Long serviceOfferingId = cmd.getServiceOfferingId(); + final List domainIds = cmd.getDomainIds(); + final List zoneIds = cmd.getZoneIds(); + + // check if valid domain + if (CollectionUtils.isNotEmpty(cmd.getDomainIds())) { + for (final Long domainId: cmd.getDomainIds()) { + if (domainDao.findById(domainId) == null) { + throw new InvalidParameterValueException("Please specify a valid domain id"); + } + } + } + + // check if valid zone + if (CollectionUtils.isNotEmpty(cmd.getZoneIds())) { + for (Long zoneId : cmd.getZoneIds()) { + if (_dcDao.findById(zoneId) == null) + throw new InvalidParameterValueException("Please specify a valid zone id"); + } + } + + return createVpcOffering(vpcOfferingName, displayText, supportedServices, + serviceCapabilitystList, serviceCapabilitystList, serviceOfferingId, + domainIds, zoneIds); + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_CREATE, eventDescription = "creating vpc offering", create = true) public VpcOffering createVpcOffering(final String name, final String displayText, final List supportedServices, final Map> serviceProviders, - final Map serviceCapabilitystList, final Long serviceOfferingId) { + final Map serviceCapabilitystList, final Long serviceOfferingId, List domainIds, List zoneIds) { + + // Filter child domains when both parent and child domains are present + List filteredDomainIds = filterChildSubDomains(domainIds); final Map> svcProviderMap = new HashMap>(); final Set defaultProviders = new HashSet(); @@ -423,6 +469,21 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis final boolean redundantRouter = isVpcOfferingRedundantRouter(serviceCapabilitystList); final VpcOffering offering = createVpcOffering(name, displayText, svcProviderMap, false, null, serviceOfferingId, supportsDistributedRouter, offersRegionLevelVPC, redundantRouter); + + if (offering != null) { + List detailsVO = new ArrayList<>(); + for (Long domainId : filteredDomainIds) { + detailsVO.add(new VpcOfferingDetailsVO(offering.getId(), ApiConstants.DOMAIN_ID, String.valueOf(domainId), false)); + } + if (CollectionUtils.isNotEmpty(zoneIds)) { + for (Long zoneId : zoneIds) { + detailsVO.add(new VpcOfferingDetailsVO(offering.getId(), ApiConstants.ZONE_ID, String.valueOf(zoneId), false)); + } + } + if (!detailsVO.isEmpty()) { + vpcOfferingDetailsDao.saveDetails(detailsVO); + } + } CallContext.current().setEventDetails(" Id: " + offering.getId() + " Name: " + name); return offering; @@ -692,44 +753,110 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_UPDATE, eventDescription = "updating vpc offering") - public VpcOffering updateVpcOffering(final long vpcOffId, final String vpcOfferingName, final String displayText, final String state) { + public VpcOffering updateVpcOffering(UpdateVPCOfferingCmd cmd) { + final Long offeringId = cmd.getId(); + final String vpcOfferingName = cmd.getVpcOfferingName(); + final String displayText = cmd.getDisplayText(); + final String state = cmd.getState(); + final List domainIds = cmd.getDomainIds(); + final List zoneIds = cmd.getZoneIds(); + + // check if valid domain + if (CollectionUtils.isNotEmpty(domainIds)) { + for (final Long domainId: domainIds) { + if (domainDao.findById(domainId) == null) { + throw new InvalidParameterValueException("Please specify a valid domain id"); + } + } + } + + // check if valid zone + if (CollectionUtils.isNotEmpty(zoneIds)) { + for (Long zoneId : zoneIds) { + if (_dcDao.findById(zoneId) == null) + throw new InvalidParameterValueException("Please specify a valid zone id"); + } + } + + return updateVpcOffering(offeringId, vpcOfferingName, displayText, state, domainIds, zoneIds); + } + + private VpcOffering updateVpcOffering(final long vpcOffId,final String vpcOfferingName, final String displayText, final String state, final List domainIds, final List zoneIds) { CallContext.current().setEventDetails(" Id: " + vpcOffId); // Verify input parameters final VpcOfferingVO offeringToUpdate = _vpcOffDao.findById(vpcOffId); + List existingDomainIds = vpcOfferingDetailsDao.findDomainIds(vpcOffId); if (offeringToUpdate == null) { throw new InvalidParameterValueException("Unable to find vpc offering " + vpcOffId); } - final VpcOfferingVO offering = _vpcOffDao.createForUpdate(vpcOffId); - if (vpcOfferingName != null) { - offering.setName(vpcOfferingName); - } - - if (displayText != null) { - offering.setDisplayText(displayText); - } - - if (state != null) { - boolean validState = false; - for (final VpcOffering.State st : VpcOffering.State.values()) { - if (st.name().equalsIgnoreCase(state)) { - validState = true; - offering.setState(st); + // Filter child domains when both parent and child domains are present + List filteredDomainIds = filterChildSubDomains(domainIds); + if (CollectionUtils.isNotEmpty(existingDomainIds) && CollectionUtils.isNotEmpty(filteredDomainIds)) { + filteredDomainIds.removeIf(existingDomainIds::contains); + for (Long domainId : filteredDomainIds) { + for (Long existingDomainId : existingDomainIds) { + if (domainDao.isChildDomain(existingDomainId, domainId)) { + throw new InvalidParameterValueException("Unable to update VPC offering for domain " + domainDao.findById(domainId).getUuid() + " as offering is already available for parent domain"); + } } } - if (!validState) { - throw new InvalidParameterValueException("Incorrect state value: " + state); + } + + List filteredZoneIds = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(zoneIds)) { + filteredZoneIds.addAll(zoneIds); + List existingZoneIds = vpcOfferingDetailsDao.findZoneIds(vpcOffId); + if (CollectionUtils.isNotEmpty(existingZoneIds)) { + filteredZoneIds.removeIf(existingZoneIds::contains); } } - if (_vpcOffDao.update(vpcOffId, offering)) { - s_logger.debug("Updated VPC offeirng id=" + vpcOffId); - return _vpcOffDao.findById(vpcOffId); - } else { - return null; + final boolean updateNeeded = vpcOfferingName != null || displayText != null || state != null; + + final VpcOfferingVO offering = _vpcOffDao.createForUpdate(vpcOffId); + + if (updateNeeded) { + if (vpcOfferingName != null) { + offering.setName(vpcOfferingName); + } + + if (displayText != null) { + offering.setDisplayText(displayText); + } + + if (state != null) { + boolean validState = false; + for (final VpcOffering.State st : VpcOffering.State.values()) { + if (st.name().equalsIgnoreCase(state)) { + validState = true; + offering.setState(st); + } + } + if (!validState) { + throw new InvalidParameterValueException("Incorrect state value: " + state); + } + } + if (!_vpcOffDao.update(vpcOffId, offering)) { + return null; + } } + List detailsVO = new ArrayList<>(); + for (Long domainId : filteredDomainIds) { + detailsVO.add(new VpcOfferingDetailsVO(vpcOffId, ApiConstants.DOMAIN_ID, String.valueOf(domainId), false)); + } + for (Long zoneId : filteredZoneIds) { + detailsVO.add(new VpcOfferingDetailsVO(vpcOffId, ApiConstants.ZONE_ID, String.valueOf(zoneId), false)); + } + if (!detailsVO.isEmpty()) { + for (VpcOfferingDetailsVO detailVO : detailsVO) { + vpcOfferingDetailsDao.persist(detailVO); + } + } + s_logger.debug("Updated VPC offeirng id=" + vpcOffId); + return _vpcOffDao.findById(vpcOffId); } @Override @@ -757,7 +884,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis throw ex; } - final boolean isRegionLevelVpcOff = vpcOff.offersRegionLevelVPC(); + final boolean isRegionLevelVpcOff = vpcOff.isOffersRegionLevelVPC(); if (isRegionLevelVpcOff && networkDomain == null) { throw new InvalidParameterValueException("Network domain must be specified for region level VPC"); } @@ -786,9 +913,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } - final boolean useDistributedRouter = vpcOff.supportsDistributedRouter(); + final boolean useDistributedRouter = vpcOff.isSupportsDistributedRouter(); final VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, owner.getId(), owner.getDomainId(), vpcOffId, cidr, networkDomain, useDistributedRouter, isRegionLevelVpcOff, - vpcOff.getRedundantRouter()); + vpcOff.isRedundantRouter()); return createVpc(displayVpc, vpc); } @@ -2502,4 +2629,27 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return _ntwkMgr.areRoutersRunning(_routerDao.listByVpcId(vpc.getId())); } + private List filterChildSubDomains(final List domainIds) { + List filteredDomainIds = new ArrayList<>(); + if (domainIds != null) { + filteredDomainIds.addAll(domainIds); + } + if (filteredDomainIds.size() > 1) { + for (int i = filteredDomainIds.size() - 1; i >= 1; i--) { + long first = filteredDomainIds.get(i); + for (int j = i - 1; j >= 0; j--) { + long second = filteredDomainIds.get(j); + if (domainDao.isChildDomain(filteredDomainIds.get(i), filteredDomainIds.get(j))) { + filteredDomainIds.remove(j); + i--; + } + if (domainDao.isChildDomain(filteredDomainIds.get(j), filteredDomainIds.get(i))) { + filteredDomainIds.remove(i); + break; + } + } + } + } + return filteredDomainIds; + } }