Public IP quarantine feature (#7378)

This commit is contained in:
Bryan Lima 2023-11-15 06:29:22 -03:00 committed by GitHub
parent c7100c3d75
commit 1f29f6f040
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1815 additions and 143 deletions

View File

@ -24,6 +24,8 @@ import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd
import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd; import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd;
import org.apache.cloudstack.api.command.admin.network.ListGuestVlansCmd; import org.apache.cloudstack.api.command.admin.network.ListGuestVlansCmd;
import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd; import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd;
import org.apache.cloudstack.api.command.user.address.RemoveQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.address.UpdateQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd;
@ -246,4 +248,8 @@ public interface NetworkService {
boolean resetNetworkPermissions(ResetNetworkPermissionsCmd resetNetworkPermissionsCmd); boolean resetNetworkPermissions(ResetNetworkPermissionsCmd resetNetworkPermissionsCmd);
void validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(final Long serviceOfferingId) throws InvalidParameterValueException; void validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(final Long serviceOfferingId) throws InvalidParameterValueException;
PublicIpQuarantine updatePublicIpAddressInQuarantine(UpdateQuarantinedIpCmd cmd);
void removePublicIpAddressFromQuarantine(RemoveQuarantinedIpCmd cmd);
} }

View File

@ -0,0 +1,36 @@
// 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;
import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
import java.util.Date;
public interface PublicIpQuarantine extends InternalIdentity, Identity {
Long getPublicIpAddressId();
Long getPreviousOwnerId();
Date getEndDate();
String getRemovalReason();
Date getRemoved();
Date getCreated();
}

View File

@ -224,6 +224,8 @@ public class ApiConstants {
public static final String INSTANCES_STATS_USER_ONLY = "instancesstatsuseronly"; public static final String INSTANCES_STATS_USER_ONLY = "instancesstatsuseronly";
public static final String PREFIX = "prefix"; public static final String PREFIX = "prefix";
public static final String PREVIOUS_ACL_RULE_ID = "previousaclruleid"; public static final String PREVIOUS_ACL_RULE_ID = "previousaclruleid";
public static final String PREVIOUS_OWNER_ID = "previousownerid";
public static final String PREVIOUS_OWNER_NAME = "previousownername";
public static final String NEXT_ACL_RULE_ID = "nextaclruleid"; public static final String NEXT_ACL_RULE_ID = "nextaclruleid";
public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash"; public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash";
public static final String IMAGE_PATH = "imagepath"; public static final String IMAGE_PATH = "imagepath";
@ -402,6 +404,7 @@ public class ApiConstants {
public static final String SHOW_CAPACITIES = "showcapacities"; public static final String SHOW_CAPACITIES = "showcapacities";
public static final String SHOW_REMOVED = "showremoved"; public static final String SHOW_REMOVED = "showremoved";
public static final String SHOW_RESOURCE_ICON = "showicon"; public static final String SHOW_RESOURCE_ICON = "showicon";
public static final String SHOW_INACTIVE = "showinactive";
public static final String SHOW_UNIQUE = "showunique"; public static final String SHOW_UNIQUE = "showunique";
public static final String SIGNATURE = "signature"; public static final String SIGNATURE = "signature";
public static final String SIGNATURE_VERSION = "signatureversion"; public static final String SIGNATURE_VERSION = "signatureversion";
@ -794,6 +797,7 @@ public class ApiConstants {
public static final String IPSEC_PSK = "ipsecpsk"; public static final String IPSEC_PSK = "ipsecpsk";
public static final String GUEST_IP = "guestip"; public static final String GUEST_IP = "guestip";
public static final String REMOVED = "removed"; public static final String REMOVED = "removed";
public static final String REMOVAL_REASON = "removalreason";
public static final String COMPLETED = "completed"; public static final String COMPLETED = "completed";
public static final String IKE_VERSION = "ikeversion"; public static final String IKE_VERSION = "ikeversion";
public static final String IKE_POLICY = "ikepolicy"; public static final String IKE_POLICY = "ikepolicy";

View File

@ -64,6 +64,7 @@ import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse;
import org.apache.cloudstack.api.response.HypervisorGuestOsNamesResponse; import org.apache.cloudstack.api.response.HypervisorGuestOsNamesResponse;
import org.apache.cloudstack.api.response.IPAddressResponse; import org.apache.cloudstack.api.response.IPAddressResponse;
import org.apache.cloudstack.api.response.IpQuarantineResponse;
import org.apache.cloudstack.api.response.ImageStoreResponse; import org.apache.cloudstack.api.response.ImageStoreResponse;
import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse;
import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse; import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse;
@ -169,6 +170,7 @@ import com.cloud.network.OvsProvider;
import com.cloud.network.PhysicalNetwork; import com.cloud.network.PhysicalNetwork;
import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.network.PhysicalNetworkServiceProvider;
import com.cloud.network.PhysicalNetworkTrafficType; import com.cloud.network.PhysicalNetworkTrafficType;
import com.cloud.network.PublicIpQuarantine;
import com.cloud.network.RemoteAccessVpn; import com.cloud.network.RemoteAccessVpn;
import com.cloud.network.RouterHealthCheckResult; import com.cloud.network.RouterHealthCheckResult;
import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.network.Site2SiteCustomerGateway;
@ -529,4 +531,6 @@ public interface ResponseGenerator {
DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId, Long hostId, Pair<Boolean, String> result); DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId, Long hostId, Pair<Boolean, String> result);
FirewallResponse createIpv6FirewallRuleResponse(FirewallRule acl); FirewallResponse createIpv6FirewallRuleResponse(FirewallRule acl);
IpQuarantineResponse createQuarantinedIpsResponse(PublicIpQuarantine publicIp);
} }

View File

@ -0,0 +1,51 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.api.command.user.address;
import com.cloud.network.PublicIpQuarantine;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.IpQuarantineResponse;
import org.apache.cloudstack.api.response.ListResponse;
@APICommand(name = "listQuarantinedIps", responseObject = IpQuarantineResponse.class, description = "List public IP addresses in quarantine.", since = "4.19",
entityType = {PublicIpQuarantine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.DomainAdmin})
public class ListQuarantinedIpsCmd extends BaseListCmd {
@Parameter(name = ApiConstants.SHOW_REMOVED, type = CommandType.BOOLEAN, description = "Show IPs removed from quarantine.")
private boolean showRemoved = false;
@Parameter(name = ApiConstants.SHOW_INACTIVE, type = CommandType.BOOLEAN, description = "Show IPs that are no longer in quarantine.")
private boolean showInactive = false;
public boolean isShowRemoved() {
return showRemoved;
}
public boolean isShowInactive() {
return showInactive;
}
@Override
public void execute() {
ListResponse<IpQuarantineResponse> response = _queryService.listQuarantinedIps(this);
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
}

View File

@ -0,0 +1,72 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.api.command.user.address;
import com.cloud.network.PublicIpQuarantine;
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.IpQuarantineResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
@APICommand(name = "removeQuarantinedIp", responseObject = IpQuarantineResponse.class, description = "Removes a public IP address from quarantine. Only IPs in active " +
"quarantine can be removed.",
since = "4.19", entityType = {PublicIpQuarantine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
authorized = {RoleType.Admin, RoleType.DomainAdmin})
public class RemoveQuarantinedIpCmd extends BaseCmd {
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IpQuarantineResponse.class, description = "The ID of the public IP address in active quarantine. " +
"Either the IP address is informed, or the ID of the IP address in quarantine.")
private Long id;
@Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "The public IP address in active quarantine. Either the IP address is informed, or the ID" +
" of the IP address in quarantine.")
private String ipAddress;
@Parameter(name = ApiConstants.REMOVAL_REASON, type = CommandType.STRING, required = true, description = "The reason for removing the public IP address from quarantine " +
"prematurely.")
private String removalReason;
public Long getId() {
return id;
}
public String getIpAddress() {
return ipAddress;
}
public String getRemovalReason() {
return removalReason;
}
@Override
public void execute() {
_networkService.removePublicIpAddressFromQuarantine(this);
final SuccessResponse response = new SuccessResponse();
response.setResponseName(getCommandName());
response.setSuccess(true);
setResponseObject(response);
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
}

View File

@ -0,0 +1,75 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.api.command.user.address;
import com.cloud.network.PublicIpQuarantine;
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.IpQuarantineResponse;
import java.util.Date;
@APICommand(name = "updateQuarantinedIp", responseObject = IpQuarantineResponse.class, description = "Updates the quarantine end date for the given public IP address.",
since = "4.19", entityType = {PublicIpQuarantine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
authorized = {RoleType.Admin, RoleType.DomainAdmin})
public class UpdateQuarantinedIpCmd extends BaseCmd {
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IpQuarantineResponse.class, description = "The ID of the public IP address in " +
"active quarantine.")
private Long id;
@Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "The public IP address in active quarantine. Either the IP address is informed, or the ID" +
" of the IP address in quarantine.")
private String ipAddress;
@Parameter(name = ApiConstants.END_DATE, type = BaseCmd.CommandType.DATE, required = true, description = "The date when the quarantine will no longer be active.")
private Date endDate;
public Long getId() {
return id;
}
public String getIpAddress() {
return ipAddress;
}
public Date getEndDate() {
return endDate;
}
@Override
public void execute() {
PublicIpQuarantine publicIpQuarantine = _networkService.updatePublicIpAddressInQuarantine(this);
if (publicIpQuarantine == null) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update public IP quarantine.");
}
IpQuarantineResponse response = _responseGenerator.createQuarantinedIpsResponse(publicIpQuarantine);
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
}

View File

@ -0,0 +1,130 @@
//Licensed to the Apache Software Foundation (ASF) under one
//or more contributor license agreements. See the NOTICE file
//distributed with this work for additional information
//regarding copyright ownership. The ASF licenses this file
//to you under the Apache License, Version 2.0 (the
//"License"); you may not use this file except in compliance
//with the License. You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing,
//software distributed under the License is distributed on an
//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//KIND, either express or implied. See the License for the
//specific language governing permissions and limitations
//under the License.
package org.apache.cloudstack.api.response;
import com.cloud.network.PublicIpQuarantine;
import com.cloud.serializer.Param;
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 java.util.Date;
@EntityReference(value = {PublicIpQuarantine.class})
public class IpQuarantineResponse extends BaseResponse {
@SerializedName(ApiConstants.ID)
@Param(description = "ID of the quarantine process.")
private String id;
@SerializedName(ApiConstants.IP_ADDRESS)
@Param(description = "The public IP address in quarantine.")
private String publicIpAddress;
@SerializedName(ApiConstants.PREVIOUS_OWNER_ID)
@Param(description = "Account ID of the previous public IP address owner.")
private String previousOwnerId;
@SerializedName(ApiConstants.PREVIOUS_OWNER_NAME)
@Param(description = "Account name of the previous public IP address owner.")
private String previousOwnerName;
@SerializedName(ApiConstants.CREATED)
@Param(description = "When the quarantine was created.")
private Date created;
@SerializedName(ApiConstants.REMOVED)
@Param(description = "When the quarantine was removed.")
private Date removed;
@SerializedName(ApiConstants.END_DATE)
@Param(description = "End date for the quarantine.")
private Date endDate;
@SerializedName(ApiConstants.REMOVAL_REASON)
@Param(description = "The reason for removing the IP from quarantine prematurely.")
private String removalReason;
public IpQuarantineResponse() {
super("quarantinedips");
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPublicIpAddress() {
return publicIpAddress;
}
public void setPublicIpAddress(String publicIpAddress) {
this.publicIpAddress = publicIpAddress;
}
public String getPreviousOwnerId() {
return previousOwnerId;
}
public void setPreviousOwnerId(String previousOwnerId) {
this.previousOwnerId = previousOwnerId;
}
public String getPreviousOwnerName() {
return previousOwnerName;
}
public void setPreviousOwnerName(String previousOwnerName) {
this.previousOwnerName = previousOwnerName;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Date getRemoved() {
return removed;
}
public void setRemoved(Date removed) {
this.removed = removed;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public String getRemovalReason() {
return removalReason;
}
public void setRemovalReason(String removalReason) {
this.removalReason = removalReason;
}
}

View File

@ -34,6 +34,7 @@ import org.apache.cloudstack.api.command.admin.storage.ListStorageTagsCmd;
import org.apache.cloudstack.api.command.admin.user.ListUsersCmd; import org.apache.cloudstack.api.command.admin.user.ListUsersCmd;
import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListAccountsCmd;
import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd;
import org.apache.cloudstack.api.command.user.address.ListQuarantinedIpsCmd;
import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd; import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd;
import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
import org.apache.cloudstack.api.command.user.iso.ListIsosCmd; import org.apache.cloudstack.api.command.user.iso.ListIsosCmd;
@ -64,6 +65,7 @@ import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.HostTagResponse; import org.apache.cloudstack.api.response.HostTagResponse;
import org.apache.cloudstack.api.response.ImageStoreResponse; import org.apache.cloudstack.api.response.ImageStoreResponse;
import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse;
import org.apache.cloudstack.api.response.IpQuarantineResponse;
import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.ManagementServerResponse; import org.apache.cloudstack.api.response.ManagementServerResponse;
import org.apache.cloudstack.api.response.ProjectAccountResponse; import org.apache.cloudstack.api.response.ProjectAccountResponse;
@ -183,6 +185,8 @@ public interface QueryService {
List<RouterHealthCheckResultResponse> listRouterHealthChecks(GetRouterHealthCheckResultsCmd cmd); List<RouterHealthCheckResultResponse> listRouterHealthChecks(GetRouterHealthCheckResultsCmd cmd);
ListResponse<IpQuarantineResponse> listQuarantinedIps(ListQuarantinedIpsCmd cmd);
ListResponse<SnapshotResponse> listSnapshots(ListSnapshotsCmd cmd); ListResponse<SnapshotResponse> listSnapshots(ListSnapshotsCmd cmd);
SnapshotResponse listSnapshot(CopySnapshotCmd cmd); SnapshotResponse listSnapshot(CopySnapshotCmd cmd);

View File

@ -16,6 +16,7 @@
// under the License. // under the License.
package com.cloud.network; package com.cloud.network;
import java.util.Date;
import java.util.List; import java.util.List;
import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse; import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse;
@ -238,5 +239,52 @@ public interface IpAddressManager {
public static final String MESSAGE_ASSIGN_IPADDR_EVENT = "Message.AssignIpAddr.Event"; public static final String MESSAGE_ASSIGN_IPADDR_EVENT = "Message.AssignIpAddr.Event";
public static final String MESSAGE_RELEASE_IPADDR_EVENT = "Message.ReleaseIpAddr.Event"; public static final String MESSAGE_RELEASE_IPADDR_EVENT = "Message.ReleaseIpAddr.Event";
/**
* Checks if the given public IP address is not in active quarantine.
* It returns `true` if:
* <ul>
* <li>The IP was never in quarantine;</li>
* <li>The IP was in quarantine, but the quarantine expired;</li>
* <li>The IP is still in quarantine; however, the new owner is the same as the previous owner, therefore, the IP can be allocated.</li>
* </ul>
*
* It returns `false` if:
* <ul>
* <li>The IP is in active quarantine and the new owner is different from the previous owner.</li>
* </ul>
*
* @param ip used to check if it is in active quarantine.
* @param account used to identify the new owner of the public IP.
* @return true if the IP can be allocated, and false otherwise.
*/
boolean canPublicIpAddressBeAllocated(IpAddress ip, Account account);
/**
* Adds the given public IP address to quarantine for the duration of the global configuration `public.ip.address.quarantine.duration` value.
*
* @param publicIpAddress to be quarantined.
* @param domainId used to retrieve the quarantine duration.
* @return the {@link PublicIpQuarantine} persisted in the database.
*/
PublicIpQuarantine addPublicIpAddressToQuarantine(IpAddress publicIpAddress, Long domainId);
/**
* Prematurely removes a public IP address from quarantine. It is required to provide a reason for removing it.
*
* @param quarantineProcessId the ID of the active quarantine process.
* @param removalReason for prematurely removing the public IP address from quarantine.
*/
void removePublicIpAddressFromQuarantine(Long quarantineProcessId, String removalReason);
/**
* Updates the end date of a public IP address in active quarantine. It can increase and decrease the duration of the quarantine.
*
* @param quarantineProcessId the ID of the quarantine process.
* @param endDate the new end date for the quarantine.
* @return the updated quarantine object.
*/
PublicIpQuarantine updatePublicIpAddressInQuarantine(Long quarantineProcessId, Date endDate);
void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO> userIps) throws Exception; void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO> userIps) throws Exception;
} }

View File

@ -21,6 +21,7 @@ import java.util.List;
import com.cloud.dc.Vlan.VlanType; import com.cloud.dc.Vlan.VlanType;
import com.cloud.network.IpAddress.State; import com.cloud.network.IpAddress.State;
import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDao;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.net.Ip; import com.cloud.utils.net.Ip;
public interface IPAddressDao extends GenericDao<IPAddressVO, Long> { public interface IPAddressDao extends GenericDao<IPAddressVO, Long> {
@ -100,4 +101,6 @@ public interface IPAddressDao extends GenericDao<IPAddressVO, Long> {
List<IPAddressVO> listByDcIdAndAssociatedNetwork(long dcId); List<IPAddressVO> listByDcIdAndAssociatedNetwork(long dcId);
List<IPAddressVO> listByNetworkId(long networkId); List<IPAddressVO> listByNetworkId(long networkId);
void buildQuarantineSearchCriteria(SearchCriteria<IPAddressVO> sc);
} }

View File

@ -24,6 +24,7 @@ import java.util.List;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.inject.Inject; import javax.inject.Inject;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDao; import org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDao;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -32,6 +33,7 @@ import com.cloud.dc.Vlan.VlanType;
import com.cloud.dc.VlanVO; import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.VlanDao; import com.cloud.dc.dao.VlanDao;
import com.cloud.network.IpAddress.State; import com.cloud.network.IpAddress.State;
import com.cloud.network.vo.PublicIpQuarantineVO;
import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.ResourceTag.ResourceObjectType;
import com.cloud.tags.dao.ResourceTagDao; import com.cloud.tags.dao.ResourceTagDao;
import com.cloud.utils.db.DB; import com.cloud.utils.db.DB;
@ -69,6 +71,9 @@ public class IPAddressDaoImpl extends GenericDaoBase<IPAddressVO, Long> implemen
@Inject @Inject
UserIpAddressDetailsDao _detailsDao; UserIpAddressDetailsDao _detailsDao;
@Inject
PublicIpQuarantineDao publicIpQuarantineDao;
// make it public for JUnit test // make it public for JUnit test
public IPAddressDaoImpl() { public IPAddressDaoImpl() {
} }
@ -534,4 +539,19 @@ public class IPAddressDaoImpl extends GenericDaoBase<IPAddressVO, Long> implemen
sc.setParameters("state", State.Allocated); sc.setParameters("state", State.Allocated);
return listBy(sc); return listBy(sc);
} }
@Override
public void buildQuarantineSearchCriteria(SearchCriteria<IPAddressVO> sc) {
long accountId = CallContext.current().getCallingAccount().getAccountId();
SearchBuilder<PublicIpQuarantineVO> listAllIpsInQuarantine = publicIpQuarantineDao.createSearchBuilder();
listAllIpsInQuarantine.and("quarantineEndDate", listAllIpsInQuarantine.entity().getEndDate(), SearchCriteria.Op.GT);
listAllIpsInQuarantine.and("previousOwnerId", listAllIpsInQuarantine.entity().getPreviousOwnerId(), Op.NEQ);
SearchCriteria<PublicIpQuarantineVO> searchCriteria = listAllIpsInQuarantine.create();
searchCriteria.setParameters("quarantineEndDate", new Date());
searchCriteria.setParameters("previousOwnerId", accountId);
Object[] quarantinedIpsIdsAllowedToUser = publicIpQuarantineDao.search(searchCriteria, null).stream().map(PublicIpQuarantineVO::getPublicIpAddressId).toArray();
sc.setParametersIfNotNull("quarantinedPublicIpsIdsNIN", quarantinedIpsIdsAllowedToUser);
}
} }

View File

@ -29,7 +29,6 @@ import javax.persistence.Id;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Temporal; import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import javax.persistence.Transient;
import com.cloud.network.IpAddress; import com.cloud.network.IpAddress;
import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDao;
@ -97,14 +96,6 @@ public class IPAddressVO implements IpAddress {
@Column(name = "is_system") @Column(name = "is_system")
private boolean system; private boolean system;
@Column(name = "account_id")
@Transient
private Long accountId = null;
@Transient
@Column(name = "domain_id")
private Long domainId = null;
@Column(name = "vpc_id") @Column(name = "vpc_id")
private Long vpcId; private Long vpcId;

View File

@ -0,0 +1,27 @@
// 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.dao;
import com.cloud.network.vo.PublicIpQuarantineVO;
import com.cloud.utils.db.GenericDao;
public interface PublicIpQuarantineDao extends GenericDao<PublicIpQuarantineVO, Long> {
PublicIpQuarantineVO findByPublicIpAddressId(long publicIpAddressId);
PublicIpQuarantineVO findByIpAddress(String publicIpAddress);
}

View File

@ -0,0 +1,71 @@
// 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.dao;
import com.cloud.network.vo.PublicIpQuarantineVO;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.JoinBuilder;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
@Component
public class PublicIpQuarantineDaoImpl extends GenericDaoBase<PublicIpQuarantineVO, Long> implements PublicIpQuarantineDao {
private SearchBuilder<PublicIpQuarantineVO> publicIpAddressByIdSearch;
private SearchBuilder<IPAddressVO> ipAddressSearchBuilder;
@Inject
IPAddressDao ipAddressDao;
@PostConstruct
public void init() {
publicIpAddressByIdSearch = createSearchBuilder();
publicIpAddressByIdSearch.and("publicIpAddressId", publicIpAddressByIdSearch.entity().getPublicIpAddressId(), SearchCriteria.Op.EQ);
ipAddressSearchBuilder = ipAddressDao.createSearchBuilder();
ipAddressSearchBuilder.and("ipAddress", ipAddressSearchBuilder.entity().getAddress(), SearchCriteria.Op.EQ);
ipAddressSearchBuilder.and("removed", ipAddressSearchBuilder.entity().getRemoved(), SearchCriteria.Op.NULL);
publicIpAddressByIdSearch.join("quarantineJoin", ipAddressSearchBuilder, ipAddressSearchBuilder.entity().getId(),
publicIpAddressByIdSearch.entity().getPublicIpAddressId(), JoinBuilder.JoinType.INNER);
ipAddressSearchBuilder.done();
publicIpAddressByIdSearch.done();
}
@Override
public PublicIpQuarantineVO findByPublicIpAddressId(long publicIpAddressId) {
SearchCriteria<PublicIpQuarantineVO> sc = publicIpAddressByIdSearch.create();
sc.setParameters("publicIpAddressId", publicIpAddressId);
final Filter filter = new Filter(PublicIpQuarantineVO.class, "created", false);
return findOneBy(sc, filter);
}
@Override
public PublicIpQuarantineVO findByIpAddress(String publicIpAddress) {
SearchCriteria<PublicIpQuarantineVO> sc = publicIpAddressByIdSearch.create();
sc.setJoinParameters("quarantineJoin", "ipAddress", publicIpAddress);
final Filter filter = new Filter(PublicIpQuarantineVO.class, "created", false);
return findOneBy(sc, filter);
}
}

View File

@ -0,0 +1,131 @@
// 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.vo;
import com.cloud.network.PublicIpQuarantine;
import com.cloud.utils.db.GenericDao;
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 javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;
import java.util.UUID;
@Entity
@Table(name = "quarantined_ips")
public class PublicIpQuarantineVO implements PublicIpQuarantine {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "uuid", nullable = false)
private String uuid = UUID.randomUUID().toString();
@Column(name = "public_ip_address_id", nullable = false)
private Long publicIpAddressId;
@Column(name = "previous_owner_id", nullable = false)
private Long previousOwnerId;
@Column(name = GenericDao.CREATED_COLUMN, nullable = false)
@Temporal(value = TemporalType.TIMESTAMP)
private Date created;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed = null;
@Column(name = "end_date", nullable = false)
@Temporal(value = TemporalType.TIMESTAMP)
private Date endDate;
@Column(name = "removal_reason")
private String removalReason = null;
public PublicIpQuarantineVO() {
}
public PublicIpQuarantineVO(Long publicIpAddressId, Long previousOwnerId, Date created, Date endDate) {
this.publicIpAddressId = publicIpAddressId;
this.previousOwnerId = previousOwnerId;
this.created = created;
this.endDate = endDate;
}
@Override
public long getId() {
return id;
}
@Override
public Long getPublicIpAddressId() {
return publicIpAddressId;
}
@Override
public Long getPreviousOwnerId() {
return previousOwnerId;
}
@Override
public Date getEndDate() {
return endDate;
}
@Override
public String getRemovalReason() {
return removalReason;
}
@Override
public String getUuid() {
return uuid;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public void setRemovalReason(String removalReason) {
this.removalReason = removalReason;
}
@Override
public Date getRemoved() {
return removed;
}
public void setRemoved(Date removed) {
this.removed = removed;
}
@Override
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
}

View File

@ -276,6 +276,7 @@
<bean id="UserVmDeployAsIsDetailsDaoImpl" class="com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDaoImpl" /> <bean id="UserVmDeployAsIsDetailsDaoImpl" class="com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDaoImpl" />
<bean id="NetworkPermissionDaoImpl" class="org.apache.cloudstack.network.dao.NetworkPermissionDaoImpl" /> <bean id="NetworkPermissionDaoImpl" class="org.apache.cloudstack.network.dao.NetworkPermissionDaoImpl" />
<bean id="PassphraseDaoImpl" class="org.apache.cloudstack.secret.dao.PassphraseDaoImpl" /> <bean id="PassphraseDaoImpl" class="org.apache.cloudstack.secret.dao.PassphraseDaoImpl" />
<bean id="publicIpQuarantineDaoImpl" class="com.cloud.network.dao.PublicIpQuarantineDaoImpl" />
<bean id="VMScheduleDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduleDaoImpl" /> <bean id="VMScheduleDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduleDaoImpl" />
<bean id="VMScheduledJobDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDaoImpl" /> <bean id="VMScheduledJobDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDaoImpl" />
<bean id="vnfTemplateDetailsDaoImpl" class="com.cloud.storage.dao.VnfTemplateDetailsDaoImpl" /> <bean id="vnfTemplateDetailsDaoImpl" class="com.cloud.storage.dao.VnfTemplateDetailsDaoImpl" />

View File

@ -136,6 +136,21 @@ UPDATE `cloud`.`console_session` SET removed=now();
-- Modify acquired column in console_session to datetime type -- Modify acquired column in console_session to datetime type
ALTER TABLE `cloud`.`console_session` DROP `acquired`, ADD `acquired` datetime COMMENT 'When the session was acquired' AFTER `host_id`; ALTER TABLE `cloud`.`console_session` DROP `acquired`, ADD `acquired` datetime COMMENT 'When the session was acquired' AFTER `host_id`;
-- IP quarantine PR#7378
CREATE TABLE IF NOT EXISTS `cloud`.`quarantined_ips` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`uuid` varchar(255) UNIQUE,
`public_ip_address_id` bigint(20) unsigned NOT NULL COMMENT 'ID of the quarantined public IP address, foreign key to `user_ip_address` table',
`previous_owner_id` bigint(20) unsigned NOT NULL COMMENT 'ID of the previous owner of the public IP address, foreign key to `account` table',
`created` datetime NOT NULL,
`removed` datetime DEFAULT NULL,
`end_date` datetime NOT NULL,
`removal_reason` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_quarantined_ips__public_ip_address_id` FOREIGN KEY(`public_ip_address_id`) REFERENCES `cloud`.`user_ip_address`(`id`),
CONSTRAINT `fk_quarantined_ips__previous_owner_id` FOREIGN KEY(`previous_owner_id`) REFERENCES `cloud`.`account`(`id`)
);
-- create_public_parameter_on_roles. #6960 -- create_public_parameter_on_roles. #6960
ALTER TABLE `cloud`.`roles` ADD COLUMN `public_role` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Indicates whether the role will be visible to all users (public) or only to root admins (private). If this parameter is not specified during the creation of the role its value will be defaulted to true (public).'; ALTER TABLE `cloud`.`roles` ADD COLUMN `public_role` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Indicates whether the role will be visible to all users (public) or only to root admins (private). If this parameter is not specified during the creation of the role its value will be defaulted to true (public).';

View File

@ -51,6 +51,10 @@ public class Filter {
addOrderBy(clazz, field, ascending); addOrderBy(clazz, field, ascending);
} }
public Filter(Class<?> clazz, String field, boolean ascending) {
this(clazz, field, ascending, null, null);
}
public Filter(long limit) { public Filter(long limit) {
_orderBy = " ORDER BY RAND() LIMIT " + limit; _orderBy = " ORDER BY RAND() LIMIT " + limit;
} }

View File

@ -258,6 +258,8 @@ public interface GenericDao<T, ID extends Serializable> {
public T findOneBy(final SearchCriteria<T> sc); public T findOneBy(final SearchCriteria<T> sc);
T findOneBy(SearchCriteria<T> sc, Filter filter);
/** /**
* @return * @return
*/ */

View File

@ -422,7 +422,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
return result; return result;
} catch (final SQLException e) { } catch (final SQLException e) {
throw new CloudRuntimeException("DB Exception on: " + pstmt, e); throw new CloudRuntimeException("DB Exception on: " + pstmt, e);
} catch (final Throwable e) { } catch (final Exception e) {
throw new CloudRuntimeException("Caught: " + pstmt, e); throw new CloudRuntimeException("Caught: " + pstmt, e);
} }
} }
@ -499,7 +499,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
return results; return results;
} catch (final SQLException e) { } catch (final SQLException e) {
throw new CloudRuntimeException("DB Exception on: " + pstmt, e); throw new CloudRuntimeException("DB Exception on: " + pstmt, e);
} catch (final Throwable e) { } catch (final Exception e) {
throw new CloudRuntimeException("Caught: " + pstmt, e); throw new CloudRuntimeException("Caught: " + pstmt, e);
} }
} }
@ -907,6 +907,15 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
return findOneIncludingRemovedBy(sc); return findOneIncludingRemovedBy(sc);
} }
@Override
@DB()
public T findOneBy(SearchCriteria<T> sc, final Filter filter) {
sc = checkAndSetRemovedIsNull(sc);
filter.setLimit(1L);
List<T> results = searchIncludingRemoved(sc, filter, null, false);
return results.isEmpty() ? null : results.get(0);
}
@DB() @DB()
protected List<T> listBy(SearchCriteria<T> sc, final Filter filter) { protected List<T> listBy(SearchCriteria<T> sc, final Filter filter) {
sc = checkAndSetRemovedIsNull(sc); sc = checkAndSetRemovedIsNull(sc);
@ -1145,7 +1154,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
return result; return result;
} catch (final SQLException e) { } catch (final SQLException e) {
throw new CloudRuntimeException("DB Exception on: " + pstmt, e); throw new CloudRuntimeException("DB Exception on: " + pstmt, e);
} catch (final Throwable e) { } catch (final Exception e) {
throw new CloudRuntimeException("Caught: " + pstmt, e); throw new CloudRuntimeException("Caught: " + pstmt, e);
} }
} }
@ -1227,7 +1236,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
return pstmt.executeUpdate(); return pstmt.executeUpdate();
} catch (final SQLException e) { } catch (final SQLException e) {
throw new CloudRuntimeException("DB Exception on: " + pstmt, e); throw new CloudRuntimeException("DB Exception on: " + pstmt, e);
} catch (final Throwable e) { } catch (final Exception e) {
throw new CloudRuntimeException("Caught: " + pstmt, e); throw new CloudRuntimeException("Caught: " + pstmt, e);
} }
} }
@ -2050,7 +2059,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
return 0; return 0;
} catch (final SQLException e) { } catch (final SQLException e) {
throw new CloudRuntimeException("DB Exception on: " + pstmt, e); throw new CloudRuntimeException("DB Exception on: " + pstmt, e);
} catch (final Throwable e) { } catch (final Exception e) {
throw new CloudRuntimeException("Caught: " + pstmt, e); throw new CloudRuntimeException("Caught: " + pstmt, e);
} }
} }
@ -2101,7 +2110,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
return 0; return 0;
} catch (final SQLException e) { } catch (final SQLException e) {
throw new CloudRuntimeException("DB Exception in executing: " + sql, e); throw new CloudRuntimeException("DB Exception in executing: " + sql, e);
} catch (final Throwable e) { } catch (final Exception e) {
throw new CloudRuntimeException("Caught exception in : " + sql, e); throw new CloudRuntimeException("Caught exception in : " + sql, e);
} }
} }
@ -2158,7 +2167,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
return 0; return 0;
} catch (final SQLException e) { } catch (final SQLException e) {
throw new CloudRuntimeException("DB Exception on: " + pstmt, e); throw new CloudRuntimeException("DB Exception on: " + pstmt, e);
} catch (final Throwable e) { } catch (final Exception e) {
throw new CloudRuntimeException("Caught: " + pstmt, e); throw new CloudRuntimeException("Caught: " + pstmt, e);
} }
} }

View File

@ -100,6 +100,7 @@ import org.apache.cloudstack.api.response.ImageStoreResponse;
import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse;
import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse; import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse;
import org.apache.cloudstack.api.response.IpForwardingRuleResponse; import org.apache.cloudstack.api.response.IpForwardingRuleResponse;
import org.apache.cloudstack.api.response.IpQuarantineResponse;
import org.apache.cloudstack.api.response.IpRangeResponse; import org.apache.cloudstack.api.response.IpRangeResponse;
import org.apache.cloudstack.api.response.Ipv6RouteResponse; import org.apache.cloudstack.api.response.Ipv6RouteResponse;
import org.apache.cloudstack.api.response.IsolationMethodResponse; import org.apache.cloudstack.api.response.IsolationMethodResponse;
@ -282,6 +283,7 @@ import com.cloud.network.OvsProvider;
import com.cloud.network.PhysicalNetwork; import com.cloud.network.PhysicalNetwork;
import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.network.PhysicalNetworkServiceProvider;
import com.cloud.network.PhysicalNetworkTrafficType; import com.cloud.network.PhysicalNetworkTrafficType;
import com.cloud.network.PublicIpQuarantine;
import com.cloud.network.RemoteAccessVpn; import com.cloud.network.RemoteAccessVpn;
import com.cloud.network.RouterHealthCheckResult; import com.cloud.network.RouterHealthCheckResult;
import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.network.Site2SiteCustomerGateway;
@ -5088,4 +5090,23 @@ public class ApiResponseHelper implements ResponseGenerator {
response.setObjectName("firewallrule"); response.setObjectName("firewallrule");
return response; return response;
} }
@Override
public IpQuarantineResponse createQuarantinedIpsResponse(PublicIpQuarantine quarantinedIp) {
IpQuarantineResponse quarantinedIpsResponse = new IpQuarantineResponse();
String ipAddress = userIpAddressDao.findById(quarantinedIp.getPublicIpAddressId()).getAddress().toString();
Account previousOwner = _accountMgr.getAccount(quarantinedIp.getPreviousOwnerId());
quarantinedIpsResponse.setId(quarantinedIp.getUuid());
quarantinedIpsResponse.setPublicIpAddress(ipAddress);
quarantinedIpsResponse.setPreviousOwnerId(previousOwner.getUuid());
quarantinedIpsResponse.setPreviousOwnerName(previousOwner.getName());
quarantinedIpsResponse.setCreated(quarantinedIp.getCreated());
quarantinedIpsResponse.setRemoved(quarantinedIp.getRemoved());
quarantinedIpsResponse.setEndDate(quarantinedIp.getEndDate());
quarantinedIpsResponse.setRemovalReason(quarantinedIp.getRemovalReason());
quarantinedIpsResponse.setResponseName("quarantinedip");
return quarantinedIpsResponse;
}
} }

View File

@ -43,6 +43,9 @@ import com.cloud.network.as.dao.AutoScaleVmGroupDao;
import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao;
import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.PublicIpQuarantineDao;
import com.cloud.network.PublicIpQuarantine;
import com.cloud.network.vo.PublicIpQuarantineVO;
import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.SSHKeyPairVO; import com.cloud.user.SSHKeyPairVO;
import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.SSHKeyPairDao;
@ -88,6 +91,7 @@ import org.apache.cloudstack.api.command.admin.user.ListUsersCmd;
import org.apache.cloudstack.api.command.admin.zone.ListZonesCmdByAdmin; import org.apache.cloudstack.api.command.admin.zone.ListZonesCmdByAdmin;
import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListAccountsCmd;
import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd;
import org.apache.cloudstack.api.command.user.address.ListQuarantinedIpsCmd;
import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd; import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd;
import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
import org.apache.cloudstack.api.command.user.iso.ListIsosCmd; import org.apache.cloudstack.api.command.user.iso.ListIsosCmd;
@ -119,6 +123,7 @@ import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.HostTagResponse; import org.apache.cloudstack.api.response.HostTagResponse;
import org.apache.cloudstack.api.response.ImageStoreResponse; import org.apache.cloudstack.api.response.ImageStoreResponse;
import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse;
import org.apache.cloudstack.api.response.IpQuarantineResponse;
import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.ManagementServerResponse; import org.apache.cloudstack.api.response.ManagementServerResponse;
import org.apache.cloudstack.api.response.ProjectAccountResponse; import org.apache.cloudstack.api.response.ProjectAccountResponse;
@ -541,6 +546,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
@Inject @Inject
EntityManager entityManager; EntityManager entityManager;
@Inject
private PublicIpQuarantineDao publicIpQuarantineDao;
private SearchCriteria<ServiceOfferingJoinVO> getMinimumCpuServiceOfferingJoinSearchCriteria(int cpu) { private SearchCriteria<ServiceOfferingJoinVO> getMinimumCpuServiceOfferingJoinSearchCriteria(int cpu) {
SearchCriteria<ServiceOfferingJoinVO> sc = _srvOfferingJoinDao.createSearchCriteria(); SearchCriteria<ServiceOfferingJoinVO> sc = _srvOfferingJoinDao.createSearchCriteria();
SearchCriteria<ServiceOfferingJoinVO> sc1 = _srvOfferingJoinDao.createSearchCriteria(); SearchCriteria<ServiceOfferingJoinVO> sc1 = _srvOfferingJoinDao.createSearchCriteria();
@ -4754,6 +4762,49 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
} }
@Override @Override
public ListResponse<IpQuarantineResponse> listQuarantinedIps(ListQuarantinedIpsCmd cmd) {
ListResponse<IpQuarantineResponse> response = new ListResponse<>();
Pair<List<PublicIpQuarantineVO>, Integer> result = listQuarantinedIpsInternal(cmd.isShowRemoved(), cmd.isShowInactive());
List<IpQuarantineResponse> ipsQuarantinedResponses = new ArrayList<>();
for (PublicIpQuarantine quarantinedIp : result.first()) {
IpQuarantineResponse ipsInQuarantineResponse = responseGenerator.createQuarantinedIpsResponse(quarantinedIp);
ipsQuarantinedResponses.add(ipsInQuarantineResponse);
}
response.setResponses(ipsQuarantinedResponses);
return response;
}
/**
* It lists the quarantine IPs that the caller account is allowed to see by filtering the domain path of the caller account.
* Furthermore, it lists inactive and removed quarantined IPs according to the command parameters.
*/
private Pair<List<PublicIpQuarantineVO>, Integer> listQuarantinedIpsInternal(boolean showRemoved, boolean showInactive) {
String callingAccountDomainPath = _domainDao.findById(CallContext.current().getCallingAccount().getDomainId()).getPath();
SearchBuilder<AccountJoinVO> filterAllowedOnly = _accountJoinDao.createSearchBuilder();
filterAllowedOnly.and("path", filterAllowedOnly.entity().getDomainPath(), SearchCriteria.Op.LIKE);
SearchBuilder<PublicIpQuarantineVO> listAllPublicIpsInQuarantineAllowedToTheCaller = publicIpQuarantineDao.createSearchBuilder();
listAllPublicIpsInQuarantineAllowedToTheCaller.join("listQuarantinedJoin", filterAllowedOnly,
listAllPublicIpsInQuarantineAllowedToTheCaller.entity().getPreviousOwnerId(),
filterAllowedOnly.entity().getId(), JoinBuilder.JoinType.INNER);
if (!showInactive) {
listAllPublicIpsInQuarantineAllowedToTheCaller.and("endDate", listAllPublicIpsInQuarantineAllowedToTheCaller.entity().getEndDate(), SearchCriteria.Op.GT);
}
filterAllowedOnly.done();
listAllPublicIpsInQuarantineAllowedToTheCaller.done();
SearchCriteria<PublicIpQuarantineVO> searchCriteria = listAllPublicIpsInQuarantineAllowedToTheCaller.create();
searchCriteria.setJoinParameters("listQuarantinedJoin", "path", callingAccountDomainPath + "%");
searchCriteria.setParametersIfNotNull("endDate", new Date());
return publicIpQuarantineDao.searchAndCount(searchCriteria, null, showRemoved);
}
public ListResponse<SnapshotResponse> listSnapshots(ListSnapshotsCmd cmd) { public ListResponse<SnapshotResponse> listSnapshots(ListSnapshotsCmd cmd) {
Account caller = CallContext.current().getCallingAccount(); Account caller = CallContext.current().getCallingAccount();
Pair<List<SnapshotJoinVO>, Integer> result = searchForSnapshotsWithParams(cmd.getId(), cmd.getIds(), Pair<List<SnapshotJoinVO>, Integer> result = searchForSnapshotsWithParams(cmd.getId(), cmd.getIds(),

View File

@ -18,11 +18,13 @@ package com.cloud.network;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -31,6 +33,8 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import com.cloud.network.dao.PublicIpQuarantineDao;
import com.cloud.network.vo.PublicIpQuarantineVO;
import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.AnnotationService;
@ -308,6 +312,9 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
@Inject @Inject
MessageBus messageBus; MessageBus messageBus;
@Inject
PublicIpQuarantineDao publicIpQuarantineDao;
SearchBuilder<IPAddressVO> AssignIpAddressSearch; SearchBuilder<IPAddressVO> AssignIpAddressSearch;
SearchBuilder<IPAddressVO> AssignIpAddressFromPodVlanSearch; SearchBuilder<IPAddressVO> AssignIpAddressFromPodVlanSearch;
private static final Object allocatedLock = new Object(); private static final Object allocatedLock = new Object();
@ -318,6 +325,9 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
Boolean.class, "system.vm.public.ip.reservation.mode.strictness", "false", Boolean.class, "system.vm.public.ip.reservation.mode.strictness", "false",
"If enabled, the use of System VMs public IP reservation is strict, preferred if not.", true, ConfigKey.Scope.Global); "If enabled, the use of System VMs public IP reservation is strict, preferred if not.", true, ConfigKey.Scope.Global);
public static final ConfigKey<Integer> PUBLIC_IP_ADDRESS_QUARANTINE_DURATION = new ConfigKey<>("Network", Integer.class, "public.ip.address.quarantine.duration",
"0", "The duration (in minutes) for the public IP address to be quarantined when it is disassociated.", true, ConfigKey.Scope.Domain);
private Random rand = new Random(System.currentTimeMillis()); private Random rand = new Random(System.currentTimeMillis());
private List<Long> getIpv6SupportingVlanRangeIds(long dcId) throws InsufficientAddressCapacityException { private List<Long> getIpv6SupportingVlanRangeIds(long dcId) throws InsufficientAddressCapacityException {
@ -523,8 +533,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
return true; return true;
} }
private IpAddress allocateIP(Account ipOwner, boolean isSystem, long zoneId) throws ResourceAllocationException, InsufficientAddressCapacityException, private IpAddress allocateIP(Account ipOwner, boolean isSystem, long zoneId) throws InsufficientAddressCapacityException, ConcurrentOperationException {
ConcurrentOperationException {
Account caller = CallContext.current().getCallingAccount(); Account caller = CallContext.current().getCallingAccount();
long callerUserId = CallContext.current().getCallingUserId(); long callerUserId = CallContext.current().getCallingUserId();
// check permissions // check permissions
@ -698,6 +707,9 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
public boolean disassociatePublicIpAddress(long addrId, long userId, Account caller) { public boolean disassociatePublicIpAddress(long addrId, long userId, Account caller) {
boolean success = true; boolean success = true;
IPAddressVO ipToBeDisassociated = _ipAddressDao.findById(addrId);
PublicIpQuarantine publicIpQuarantine = null;
// Cleanup all ip address resources - PF/LB/Static nat rules // Cleanup all ip address resources - PF/LB/Static nat rules
if (!cleanupIpResources(addrId, userId, caller)) { if (!cleanupIpResources(addrId, userId, caller)) {
success = false; success = false;
@ -723,10 +735,9 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
} catch (ResourceUnavailableException e) { } catch (ResourceUnavailableException e) {
throw new CloudRuntimeException("We should never get to here because we used true when applyIpAssociations", e); throw new CloudRuntimeException("We should never get to here because we used true when applyIpAssociations", e);
} }
} else { } else if (ip.getState() == State.Releasing) {
if (ip.getState() == IpAddress.State.Releasing) { publicIpQuarantine = addPublicIpAddressToQuarantine(ipToBeDisassociated, caller.getDomainId());
_ipAddressDao.unassignIpAddress(ip.getId()); _ipAddressDao.unassignIpAddress(ip.getId());
}
} }
annotationDao.removeByEntityType(AnnotationService.EntityType.PUBLIC_IP_ADDRESS.name(), ip.getUuid()); annotationDao.removeByEntityType(AnnotationService.EntityType.PUBLIC_IP_ADDRESS.name(), ip.getUuid());
@ -736,6 +747,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
releasePortableIpAddress(addrId); releasePortableIpAddress(addrId);
} }
s_logger.debug("Released a public ip id=" + addrId); s_logger.debug("Released a public ip id=" + addrId);
} else if (publicIpQuarantine != null) {
removePublicIpAddressFromQuarantine(publicIpQuarantine.getId(), "Public IP address removed from quarantine as there was an error while disassociating it.");
} }
return success; return success;
@ -972,6 +985,13 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
if (lockOneRow) { if (lockOneRow) {
assert (addrs.size() == 1) : "Return size is incorrect: " + addrs.size(); assert (addrs.size() == 1) : "Return size is incorrect: " + addrs.size();
IpAddress ipAddress = addrs.get(0);
boolean ipCanBeAllocated = canPublicIpAddressBeAllocated(ipAddress, owner);
if (!ipCanBeAllocated) {
throw new InsufficientAddressCapacityException(String.format("Failed to allocate public IP address [%s] as it is in quarantine.", ipAddress.getAddress()),
DataCenter.class, dcId);
}
} }
if (assign && !fetchFromDedicatedRange && VlanType.VirtualNetwork.equals(vlanUse)) { if (assign && !fetchFromDedicatedRange && VlanType.VirtualNetwork.equals(vlanUse)) {
@ -1126,6 +1146,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
} else if (addr.getState() == IpAddress.State.Releasing) { } else if (addr.getState() == IpAddress.State.Releasing) {
// Cleanup all the resources for ip address if there are any, and only then un-assign ip in the system // Cleanup all the resources for ip address if there are any, and only then un-assign ip in the system
if (cleanupIpResources(addr.getId(), Account.ACCOUNT_ID_SYSTEM, _accountMgr.getSystemAccount())) { if (cleanupIpResources(addr.getId(), Account.ACCOUNT_ID_SYSTEM, _accountMgr.getSystemAccount())) {
addPublicIpAddressToQuarantine(addr, network.getDomainId());
_ipAddressDao.unassignIpAddress(addr.getId()); _ipAddressDao.unassignIpAddress(addr.getId());
messageBus.publish(_name, MESSAGE_RELEASE_IPADDR_EVENT, PublishScope.LOCAL, addr); messageBus.publish(_name, MESSAGE_RELEASE_IPADDR_EVENT, PublishScope.LOCAL, addr);
} else { } else {
@ -1258,8 +1279,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
@DB @DB
@Override @Override
public IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Account caller, long callerUserId, final DataCenter zone, final Boolean displayIp, final String ipaddress) public IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Account caller, long callerUserId, final DataCenter zone, final Boolean displayIp, final String ipaddress)
throws ConcurrentOperationException, throws ConcurrentOperationException, InsufficientAddressCapacityException, CloudRuntimeException {
ResourceAllocationException, InsufficientAddressCapacityException {
final VlanType vlanType = VlanType.VirtualNetwork; final VlanType vlanType = VlanType.VirtualNetwork;
final boolean assign = false; final boolean assign = false;
@ -2347,7 +2367,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
@Override @Override
public ConfigKey<?>[] getConfigKeys() { public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {UseSystemPublicIps, RulesContinueOnError, SystemVmPublicIpReservationModeStrictness, VrouterRedundantTiersPlacement, AllowUserListAvailableIpsOnSharedNetwork}; return new ConfigKey<?>[] {UseSystemPublicIps, RulesContinueOnError, SystemVmPublicIpReservationModeStrictness, VrouterRedundantTiersPlacement, AllowUserListAvailableIpsOnSharedNetwork,
PUBLIC_IP_ADDRESS_QUARANTINE_DURATION};
} }
/** /**
@ -2381,6 +2402,96 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
return SystemVmPublicIpReservationModeStrictness; return SystemVmPublicIpReservationModeStrictness;
} }
@Override
public boolean canPublicIpAddressBeAllocated(IpAddress ip, Account newOwner) {
PublicIpQuarantineVO publicIpQuarantineVO = publicIpQuarantineDao.findByPublicIpAddressId(ip.getId());
if (publicIpQuarantineVO == null) {
s_logger.debug(String.format("Public IP address [%s] is not in quarantine; therefore, it is allowed to be allocated.", ip));
return true;
}
if (!isPublicIpAddressStillInQuarantine(publicIpQuarantineVO, new Date())) {
s_logger.debug(String.format("Public IP address [%s] is no longer in quarantine; therefore, it is allowed to be allocated.", ip));
return true;
}
Account previousOwner = _accountMgr.getAccount(publicIpQuarantineVO.getPreviousOwnerId());
if (Objects.equals(previousOwner.getUuid(), newOwner.getUuid())) {
s_logger.debug(String.format("Public IP address [%s] is in quarantine; however, the Public IP previous owner [%s] is the same as the new owner [%s]; therefore the IP" +
" can be allocated. The public IP address will be removed from quarantine.", ip, previousOwner, newOwner));
removePublicIpAddressFromQuarantine(publicIpQuarantineVO.getId(), "IP was removed from quarantine because it has been allocated by the previous owner");
return true;
}
s_logger.error(String.format("Public IP address [%s] is in quarantine and the previous owner [%s] is different than the new owner [%s]; therefore, the IP cannot be " +
"allocated.", ip, previousOwner, newOwner));
return false;
}
public boolean isPublicIpAddressStillInQuarantine(PublicIpQuarantineVO publicIpQuarantineVO, Date currentDate) {
Date quarantineEndDate = publicIpQuarantineVO.getEndDate();
Date removedDate = publicIpQuarantineVO.getRemoved();
boolean hasQuarantineEndedEarly = removedDate != null;
return hasQuarantineEndedEarly && currentDate.before(removedDate) ||
!hasQuarantineEndedEarly && currentDate.before(quarantineEndDate);
}
@Override
public PublicIpQuarantine addPublicIpAddressToQuarantine(IpAddress publicIpAddress, Long domainId) {
Integer quarantineDuration = PUBLIC_IP_ADDRESS_QUARANTINE_DURATION.valueInDomain(domainId);
if (quarantineDuration <= 0) {
s_logger.debug(String.format("Not adding IP [%s] to quarantine because configuration [%s] has value equal or less to 0.", publicIpAddress.getAddress(),
PUBLIC_IP_ADDRESS_QUARANTINE_DURATION.key()));
return null;
}
long ipId = publicIpAddress.getId();
long accountId = publicIpAddress.getAccountId();
if (accountId == Account.ACCOUNT_ID_SYSTEM) {
s_logger.debug(String.format("Not adding IP [%s] to quarantine because it belongs to the system account.", publicIpAddress.getAddress()));
return null;
}
Date currentDate = new Date();
Calendar quarantineEndDate = Calendar.getInstance();
quarantineEndDate.setTime(currentDate);
quarantineEndDate.add(Calendar.MINUTE, quarantineDuration);
PublicIpQuarantineVO publicIpQuarantine = new PublicIpQuarantineVO(ipId, accountId, currentDate, quarantineEndDate.getTime());
s_logger.debug(String.format("Adding public IP Address [%s] to quarantine for the duration of [%s] minute(s).", publicIpAddress.getAddress(), quarantineDuration));
return publicIpQuarantineDao.persist(publicIpQuarantine);
}
@Override
public void removePublicIpAddressFromQuarantine(Long quarantineProcessId, String removalReason) {
PublicIpQuarantineVO publicIpQuarantineVO = publicIpQuarantineDao.findById(quarantineProcessId);
Ip ipAddress = _ipAddressDao.findById(publicIpQuarantineVO.getPublicIpAddressId()).getAddress();
Date removedDate = new Date();
publicIpQuarantineVO.setRemoved(removedDate);
publicIpQuarantineVO.setRemovalReason(removalReason);
s_logger.debug(String.format("Removing public IP Address [%s] from quarantine by updating the removed date to [%s].", ipAddress, removedDate));
publicIpQuarantineDao.persist(publicIpQuarantineVO);
}
@Override
public PublicIpQuarantine updatePublicIpAddressInQuarantine(Long quarantineProcessId, Date newEndDate) {
PublicIpQuarantineVO publicIpQuarantineVO = publicIpQuarantineDao.findById(quarantineProcessId);
Ip ipAddress = _ipAddressDao.findById(publicIpQuarantineVO.getPublicIpAddressId()).getAddress();
Date currentEndDate = publicIpQuarantineVO.getEndDate();
publicIpQuarantineVO.setEndDate(newEndDate);
s_logger.debug(String.format("Updating the end date for the quarantine of the public IP Address [%s] from [%s] to [%s].", ipAddress, currentEndDate, newEndDate));
publicIpQuarantineDao.persist(publicIpQuarantineVO);
return publicIpQuarantineVO;
}
@Override @Override
public void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO> userIps) throws Exception{ public void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO> userIps) throws Exception{
Transaction.execute((TransactionCallbackWithException<IpAddress, Exception>) status -> { Transaction.execute((TransactionCallbackWithException<IpAddress, Exception>) status -> {

View File

@ -41,6 +41,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import com.cloud.network.dao.PublicIpQuarantineDao;
import com.cloud.offering.ServiceOffering; import com.cloud.offering.ServiceOffering;
import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDao;
import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.ControlledEntity.ACLType;
@ -55,6 +56,8 @@ import org.apache.cloudstack.api.command.admin.network.ListGuestVlansCmd;
import org.apache.cloudstack.api.command.admin.network.ListNetworksCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.ListNetworksCmdByAdmin;
import org.apache.cloudstack.api.command.admin.network.UpdateNetworkCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkCmdByAdmin;
import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd; import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd;
import org.apache.cloudstack.api.command.user.address.RemoveQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.address.UpdateQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd;
@ -401,6 +404,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
CommandSetupHelper commandSetupHelper; CommandSetupHelper commandSetupHelper;
@Inject @Inject
ServiceOfferingDao serviceOfferingDao; ServiceOfferingDao serviceOfferingDao;
@Inject
PublicIpQuarantineDao publicIpQuarantineDao;
@Autowired @Autowired
@Qualifier("networkHelper") @Qualifier("networkHelper")
@ -5939,4 +5944,75 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
public ConfigKey<?>[] getConfigKeys() { public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {AllowDuplicateNetworkName, AllowEmptyStartEndIpAddress, VRPrivateInterfaceMtu, VRPublicInterfaceMtu, AllowUsersToSpecifyVRMtu}; return new ConfigKey<?>[] {AllowDuplicateNetworkName, AllowEmptyStartEndIpAddress, VRPrivateInterfaceMtu, VRPublicInterfaceMtu, AllowUsersToSpecifyVRMtu};
} }
@Override
public PublicIpQuarantine updatePublicIpAddressInQuarantine(UpdateQuarantinedIpCmd cmd) throws CloudRuntimeException {
Long ipId = cmd.getId();
String ipAddress = cmd.getIpAddress();
Date newEndDate = cmd.getEndDate();
if (new Date().after(newEndDate)) {
throw new InvalidParameterValueException(String.format("The given end date [%s] is invalid as it is before the current date.", newEndDate));
}
PublicIpQuarantine publicIpQuarantine = retrievePublicIpQuarantine(ipId, ipAddress);
checkCallerForPublicIpQuarantineAccess(publicIpQuarantine);
String publicIpQuarantineAddress = _ipAddressDao.findById(publicIpQuarantine.getPublicIpAddressId()).getAddress().toString();
Date currentEndDate = publicIpQuarantine.getEndDate();
if (new Date().after(currentEndDate)) {
throw new CloudRuntimeException(String.format("The quarantine for the public IP address [%s] is no longer active; thus, it cannot be updated.", publicIpQuarantineAddress));
}
return _ipAddrMgr.updatePublicIpAddressInQuarantine(publicIpQuarantine.getId(), newEndDate);
}
@Override
public void removePublicIpAddressFromQuarantine(RemoveQuarantinedIpCmd cmd) throws CloudRuntimeException {
Long ipId = cmd.getId();
String ipAddress = cmd.getIpAddress();
PublicIpQuarantine publicIpQuarantine = retrievePublicIpQuarantine(ipId, ipAddress);
String removalReason = cmd.getRemovalReason();
if (StringUtils.isBlank(removalReason)) {
s_logger.error("The removalReason parameter cannot be blank.");
ipAddress = ObjectUtils.defaultIfNull(ipAddress, _ipAddressDao.findById(publicIpQuarantine.getPublicIpAddressId()).getAddress().toString());
throw new CloudRuntimeException(String.format("The given reason for removing the public IP address [%s] from quarantine is blank.", ipAddress));
}
checkCallerForPublicIpQuarantineAccess(publicIpQuarantine);
_ipAddrMgr.removePublicIpAddressFromQuarantine(publicIpQuarantine.getId(), removalReason);
}
/**
* Retrieves the active quarantine for the given public IP address. It can find by the ID of the quarantine or the address of the public IP.
* @throws CloudRuntimeException if it does not find an active quarantine for the given public IP.
*/
protected PublicIpQuarantine retrievePublicIpQuarantine(Long ipId, String ipAddress) throws CloudRuntimeException {
PublicIpQuarantine publicIpQuarantine;
if (ipId != null) {
s_logger.debug("The ID of the IP in quarantine was informed; therefore, the `ipAddress` parameter will be ignored.");
publicIpQuarantine = publicIpQuarantineDao.findById(ipId);
} else if (ipAddress != null) {
s_logger.debug("The address of the IP in quarantine was informed, it will be used to fetch its metadata.");
publicIpQuarantine = publicIpQuarantineDao.findByIpAddress(ipAddress);
} else {
throw new CloudRuntimeException("Either the ID or the address of the IP in quarantine must be informed.");
}
if (publicIpQuarantine == null) {
throw new CloudRuntimeException("There is no active quarantine for the specified IP address.");
}
return publicIpQuarantine;
}
protected void checkCallerForPublicIpQuarantineAccess(PublicIpQuarantine publicIpQuarantine) {
Account callingAccount = CallContext.current().getCallingAccount();
DomainVO domainOfThePreviousOwner = _domainDao.findById(_accountDao.findById(publicIpQuarantine.getPreviousOwnerId()).getDomainId());
_accountMgr.checkAccess(callingAccount, domainOfThePreviousOwner);
}
} }

View File

@ -1105,8 +1105,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
Vpc vpc = createVpc(cmd.getZoneId(), cmd.getVpcOffering(), cmd.getEntityOwnerId(), cmd.getVpcName(), cmd.getDisplayText(), Vpc vpc = createVpc(cmd.getZoneId(), cmd.getVpcOffering(), cmd.getEntityOwnerId(), cmd.getVpcName(), cmd.getDisplayText(),
cmd.getCidr(), cmd.getNetworkDomain(), cmd.getIp4Dns1(), cmd.getIp4Dns2(), cmd.getIp6Dns1(), cmd.getCidr(), cmd.getNetworkDomain(), cmd.getIp4Dns1(), cmd.getIp4Dns2(), cmd.getIp6Dns1(),
cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu()); cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu());
// associate cmd.getSourceNatIP() with this vpc
allocateSourceNatIp(vpc, cmd.getSourceNatIP()); String sourceNatIP = cmd.getSourceNatIP();
if (sourceNatIP != null) {
s_logger.info(String.format("Trying to allocate the specified IP [%s] as the source NAT of VPC [%s].", sourceNatIP, vpc));
allocateSourceNatIp(vpc, sourceNatIP);
}
return vpc; return vpc;
} }

View File

@ -43,6 +43,7 @@ import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import com.cloud.network.dao.PublicIpQuarantineDao;
import com.cloud.hypervisor.HypervisorGuru; import com.cloud.hypervisor.HypervisorGuru;
import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.acl.SecurityChecker;
@ -329,9 +330,12 @@ import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd;
import org.apache.cloudstack.api.command.user.address.AssociateIPAddrCmd; import org.apache.cloudstack.api.command.user.address.AssociateIPAddrCmd;
import org.apache.cloudstack.api.command.user.address.DisassociateIPAddrCmd; import org.apache.cloudstack.api.command.user.address.DisassociateIPAddrCmd;
import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd; import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd;
import org.apache.cloudstack.api.command.user.address.ListQuarantinedIpsCmd;
import org.apache.cloudstack.api.command.user.address.ReleaseIPAddrCmd; import org.apache.cloudstack.api.command.user.address.ReleaseIPAddrCmd;
import org.apache.cloudstack.api.command.user.address.RemoveQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.address.ReserveIPAddrCmd; import org.apache.cloudstack.api.command.user.address.ReserveIPAddrCmd;
import org.apache.cloudstack.api.command.user.address.UpdateIPAddrCmd; import org.apache.cloudstack.api.command.user.address.UpdateIPAddrCmd;
import org.apache.cloudstack.api.command.user.address.UpdateQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.affinitygroup.CreateAffinityGroupCmd; import org.apache.cloudstack.api.command.user.affinitygroup.CreateAffinityGroupCmd;
import org.apache.cloudstack.api.command.user.affinitygroup.DeleteAffinityGroupCmd; import org.apache.cloudstack.api.command.user.affinitygroup.DeleteAffinityGroupCmd;
import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupTypesCmd; import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupTypesCmd;
@ -979,6 +983,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Inject @Inject
UserDataManager userDataManager; UserDataManager userDataManager;
@Inject
private PublicIpQuarantineDao publicIpQuarantineDao;
private LockControllerListener _lockControllerListener; private LockControllerListener _lockControllerListener;
private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker")); private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker"));
private final ScheduledExecutorService _alertExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AlertChecker")); private final ScheduledExecutorService _alertExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AlertChecker"));
@ -2508,10 +2515,12 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
final SearchBuilder<IPAddressVO> sb2 = _publicIpAddressDao.createSearchBuilder(); final SearchBuilder<IPAddressVO> sb2 = _publicIpAddressDao.createSearchBuilder();
buildParameters(sb2, cmd, false); buildParameters(sb2, cmd, false);
sb2.and("ids", sb2.entity().getId(), SearchCriteria.Op.IN); sb2.and("ids", sb2.entity().getId(), SearchCriteria.Op.IN);
sb2.and("quarantinedPublicIpsIdsNIN", sb2.entity().getId(), SearchCriteria.Op.NIN);
SearchCriteria<IPAddressVO> sc2 = sb2.create(); SearchCriteria<IPAddressVO> sc2 = sb2.create();
setParameters(sc2, cmd, vlanType, isAllocated); setParameters(sc2, cmd, vlanType, isAllocated);
sc2.setParameters("ids", freeAddrIds.toArray()); sc2.setParameters("ids", freeAddrIds.toArray());
_publicIpAddressDao.buildQuarantineSearchCriteria(sc2);
addrs.addAll(_publicIpAddressDao.search(sc2, searchFilter)); // Allocated + Free addrs.addAll(_publicIpAddressDao.search(sc2, searchFilter)); // Allocated + Free
} }
Collections.sort(addrs, Comparator.comparing(IPAddressVO::getAddress)); Collections.sort(addrs, Comparator.comparing(IPAddressVO::getAddress));
@ -3798,6 +3807,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
cmdList.add(UpdateRemoteAccessVpnCmd.class); cmdList.add(UpdateRemoteAccessVpnCmd.class);
cmdList.add(UpdateVpnConnectionCmd.class); cmdList.add(UpdateVpnConnectionCmd.class);
cmdList.add(UpdateVpnGatewayCmd.class); cmdList.add(UpdateVpnGatewayCmd.class);
cmdList.add(ListQuarantinedIpsCmd.class);
cmdList.add(UpdateQuarantinedIpCmd.class);
cmdList.add(RemoveQuarantinedIpCmd.class);
// separated admin commands // separated admin commands
cmdList.add(ListAccountsCmdByAdmin.class); cmdList.add(ListAccountsCmdByAdmin.class);
cmdList.add(ListZonesCmdByAdmin.class); cmdList.add(ListZonesCmdByAdmin.class);

View File

@ -17,19 +17,25 @@
package com.cloud.network; package com.cloud.network;
import com.cloud.exception.ResourceUnavailableException; import static org.junit.Assert.assertFalse;
import com.cloud.network.Network.Service; import static org.junit.Assert.assertTrue;
import com.cloud.network.dao.IPAddressDao; import static org.mockito.Matchers.anyLong;
import com.cloud.network.dao.IPAddressVO; import static org.mockito.Mockito.doReturn;
import com.cloud.network.dao.NetworkDao; import static org.mockito.Mockito.lenient;
import com.cloud.network.dao.NetworkVO; import static org.mockito.Mockito.mock;
import com.cloud.network.rules.StaticNat; import static org.mockito.Mockito.verify;
import com.cloud.network.rules.StaticNatImpl; import static org.mockito.Mockito.when;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import com.cloud.network.dao.PublicIpQuarantineDao;
import com.cloud.network.vo.PublicIpQuarantineVO;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountVO; import com.cloud.user.AccountManager;
import com.cloud.utils.net.Ip;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -40,19 +46,18 @@ import org.mockito.Mockito;
import org.mockito.Spy; import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import java.util.ArrayList; import com.cloud.exception.ResourceUnavailableException;
import java.util.Collections; import com.cloud.network.Network.Service;
import java.util.List; import com.cloud.network.dao.IPAddressDao;
import java.util.Vector; import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import static org.junit.Assert.assertFalse; import com.cloud.network.dao.NetworkVO;
import static org.junit.Assert.assertTrue; import com.cloud.network.rules.StaticNat;
import static org.mockito.Matchers.anyLong; import com.cloud.network.rules.StaticNatImpl;
import static org.mockito.Mockito.doReturn; import com.cloud.offerings.NetworkOfferingVO;
import static org.mockito.Mockito.lenient; import com.cloud.offerings.dao.NetworkOfferingDao;
import static org.mockito.Mockito.mock; import com.cloud.user.AccountVO;
import static org.mockito.Mockito.verify; import com.cloud.utils.net.Ip;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class IpAddressManagerTest { public class IpAddressManagerTest {
@ -80,6 +85,34 @@ public class IpAddressManagerTest {
AccountVO account; AccountVO account;
@Mock
PublicIpQuarantineVO publicIpQuarantineVOMock;
@Mock
PublicIpQuarantineDao publicIpQuarantineDaoMock;
@Mock
IpAddress ipAddressMock;
@Mock
AccountVO newOwnerMock;
@Mock
AccountVO previousOwnerMock;
@Mock
AccountManager accountManagerMock;
final long dummyID = 1L;
final String UUID = "uuid";
private static final Date currentDate = new Date(100L);
private static final Date beforeCurrentDate = new Date(99L);
private static final Date afterCurrentDate = new Date(101L);
@Before @Before
public void setup() throws ResourceUnavailableException { public void setup() throws ResourceUnavailableException {
@ -234,6 +267,136 @@ public class IpAddressManagerTest {
return network; return network;
} }
@Test
public void isPublicIpAddressStillInQuarantineTestRemovedDateIsNullAndCurrentDateIsEqualToEndDateShouldReturnFalse() {
Date endDate = currentDate;
Mockito.when(publicIpQuarantineVOMock.getRemoved()).thenReturn(null);
Mockito.when(publicIpQuarantineVOMock.getEndDate()).thenReturn(endDate);
boolean result = ipAddressManager.isPublicIpAddressStillInQuarantine(publicIpQuarantineVOMock, currentDate);
Assert.assertFalse(result);
}
@Test
public void isPublicIpAddressStillInQuarantineTestRemovedDateIsNullAndEndDateIsBeforeCurrentDateShouldReturnFalse() {
Date endDate = beforeCurrentDate;
Mockito.when(publicIpQuarantineVOMock.getRemoved()).thenReturn(null);
Mockito.when(publicIpQuarantineVOMock.getEndDate()).thenReturn(endDate);
boolean result = ipAddressManager.isPublicIpAddressStillInQuarantine(publicIpQuarantineVOMock, currentDate);
Assert.assertFalse(result);
}
@Test
public void isPublicIpAddressStillInQuarantineTestRemovedDateIsNullAndEndDateIsAfterCurrentDateShouldReturnTrue() {
Date endDate = afterCurrentDate;
Mockito.when(publicIpQuarantineVOMock.getRemoved()).thenReturn(null);
Mockito.when(publicIpQuarantineVOMock.getEndDate()).thenReturn(endDate);
boolean result = ipAddressManager.isPublicIpAddressStillInQuarantine(publicIpQuarantineVOMock, currentDate);
Assert.assertTrue(result);
}
@Test
public void isPublicIpAddressStillInQuarantineTestRemovedDateIsEqualCurrentDateShouldReturnFalse() {
Date removedDate = currentDate;
Mockito.when(publicIpQuarantineVOMock.getEndDate()).thenReturn(currentDate);
Mockito.when(publicIpQuarantineVOMock.getRemoved()).thenReturn(removedDate);
boolean result = ipAddressManager.isPublicIpAddressStillInQuarantine(publicIpQuarantineVOMock, currentDate);
Assert.assertFalse(result);
}
@Test
public void isPublicIpAddressStillInQuarantineTestRemovedDateIsBeforeCurrentDateShouldReturnFalse() {
Date removedDate = beforeCurrentDate;
Mockito.when(publicIpQuarantineVOMock.getRemoved()).thenReturn(removedDate);
Mockito.when(publicIpQuarantineVOMock.getEndDate()).thenReturn(null);
boolean result = ipAddressManager.isPublicIpAddressStillInQuarantine(publicIpQuarantineVOMock, currentDate);
Assert.assertFalse(result);
}
@Test
public void isPublicIpAddressStillInQuarantineTestRemovedDateIsAfterCurrentDateShouldReturnTrue() {
Date removedDate = afterCurrentDate;
Mockito.when(publicIpQuarantineVOMock.getRemoved()).thenReturn(removedDate);
Mockito.when(publicIpQuarantineVOMock.getEndDate()).thenReturn(null);
boolean result = ipAddressManager.isPublicIpAddressStillInQuarantine(publicIpQuarantineVOMock, currentDate);
Assert.assertTrue(result);
}
@Test
public void checkIfPublicIpAddressIsNotInQuarantineAndCanBeAllocatedTestIpIsNotInQuarantineShouldReturnTrue() {
Mockito.when(ipAddressMock.getId()).thenReturn(dummyID);
Mockito.when(publicIpQuarantineDaoMock.findByPublicIpAddressId(Mockito.anyLong())).thenReturn(null);
boolean result = ipAddressManager.canPublicIpAddressBeAllocated(ipAddressMock, account);
Assert.assertTrue(result);
}
@Test
public void checkIfPublicIpAddressIsNotInQuarantineAndCanBeAllocatedTestIpIsNoLongerInQuarantineShouldReturnTrue() {
Mockito.when(ipAddressMock.getId()).thenReturn(dummyID);
Mockito.when(publicIpQuarantineDaoMock.findByPublicIpAddressId(Mockito.anyLong())).thenReturn(publicIpQuarantineVOMock);
Mockito.doReturn(false).when(ipAddressManager).isPublicIpAddressStillInQuarantine(Mockito.any(PublicIpQuarantineVO.class), Mockito.any(Date.class));
boolean result = ipAddressManager.canPublicIpAddressBeAllocated(ipAddressMock, newOwnerMock);
Assert.assertTrue(result);
}
@Test
public void checkIfPublicIpAddressIsNotInQuarantineAndCanBeAllocatedTestIpIsInQuarantineAndThePreviousOwnerIsTheSameAsTheNewOwnerShouldReturnTrue() {
Mockito.when(ipAddressMock.getId()).thenReturn(dummyID);
Mockito.when(publicIpQuarantineDaoMock.findByPublicIpAddressId(Mockito.anyLong())).thenReturn(publicIpQuarantineVOMock);
Mockito.doReturn(true).when(ipAddressManager).isPublicIpAddressStillInQuarantine(Mockito.any(PublicIpQuarantineVO.class), Mockito.any(Date.class));
Mockito.doNothing().when(ipAddressManager).removePublicIpAddressFromQuarantine(Mockito.anyLong(), Mockito.anyString());
Mockito.when(publicIpQuarantineVOMock.getPreviousOwnerId()).thenReturn(dummyID);
Mockito.when(accountManagerMock.getAccount(Mockito.anyLong())).thenReturn(previousOwnerMock);
Mockito.when(previousOwnerMock.getUuid()).thenReturn(UUID);
Mockito.when(newOwnerMock.getUuid()).thenReturn(UUID);
boolean result = ipAddressManager.canPublicIpAddressBeAllocated(ipAddressMock, newOwnerMock);
Assert.assertTrue(result);
}
@Test
public void checkIfPublicIpAddressIsNotInQuarantineAndCanBeAllocatedTestIpIsInQuarantineAndThePreviousOwnerIsDifferentFromTheNewOwnerShouldReturnFalse() {
final String UUID_2 = "uuid_2";
Mockito.when(ipAddressMock.getId()).thenReturn(dummyID);
Mockito.when(publicIpQuarantineDaoMock.findByPublicIpAddressId(Mockito.anyLong())).thenReturn(publicIpQuarantineVOMock);
Mockito.doReturn(true).when(ipAddressManager).isPublicIpAddressStillInQuarantine(Mockito.any(PublicIpQuarantineVO.class), Mockito.any(Date.class));
Mockito.when(publicIpQuarantineVOMock.getPreviousOwnerId()).thenReturn(dummyID);
Mockito.when(accountManagerMock.getAccount(Mockito.anyLong())).thenReturn(previousOwnerMock);
Mockito.when(previousOwnerMock.getUuid()).thenReturn(UUID);
Mockito.when(newOwnerMock.getUuid()).thenReturn(UUID_2);
boolean result = ipAddressManager.canPublicIpAddressBeAllocated(ipAddressMock, newOwnerMock);
Assert.assertFalse(result);
}
@Test @Test
public void updateSourceNatIpAddress() throws Exception { public void updateSourceNatIpAddress() throws Exception {
IPAddressVO requestedIp = Mockito.mock(IPAddressVO.class); IPAddressVO requestedIp = Mockito.mock(IPAddressVO.class);

View File

@ -16,13 +16,60 @@
// under the License. // under the License.
package com.cloud.network; package com.cloud.network;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.doReturn;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.network.dao.PublicIpQuarantineDao;
import com.cloud.network.vo.PublicIpQuarantineVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.net.Ip;
import com.cloud.exception.InsufficientAddressCapacityException;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.api.command.user.address.UpdateQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.test.util.ReflectionTestUtils;
import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.IpAddressTO;
import com.cloud.alert.AlertManager; import com.cloud.alert.AlertManager;
import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.ConfigurationManager;
import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterDao;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceAllocationException;
@ -60,44 +107,9 @@ import com.cloud.vm.NicVO;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicDao;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic; import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class NetworkServiceImplTest { public class NetworkServiceImplTest {
@ -157,7 +169,7 @@ public class NetworkServiceImplTest {
ServiceOfferingVO serviceOfferingVoMock; ServiceOfferingVO serviceOfferingVoMock;
@Mock @Mock
IpAddressManager ipAddressManager; ConfigKey<Integer> privateMtuKey;
@Mock @Mock
private CallContext callContextMock; private CallContext callContextMock;
@InjectMocks @InjectMocks
@ -169,9 +181,46 @@ public class NetworkServiceImplTest {
CommandSetupHelper commandSetupHelper; CommandSetupHelper commandSetupHelper;
@Mock @Mock
private Account accountMock; private Account accountMock;
@Mock
private AccountVO accountVOMock;
@Mock
private DomainVO domainVOMock;
@InjectMocks @InjectMocks
NetworkServiceImpl service = new NetworkServiceImpl(); NetworkServiceImpl service = new NetworkServiceImpl();
@Mock
DomainDao domainDaoMock;
@Mock
AccountDao accountDaoMock;
@Mock
UpdateQuarantinedIpCmd updateQuarantinedIpCmdMock;
@Mock
PublicIpQuarantineDao publicIpQuarantineDaoMock;
@Mock
private PublicIpQuarantineVO publicIpQuarantineVOMock;
@Mock
private IPAddressVO ipAddressVOMock;
@Mock
private IpAddressManager ipAddressManagerMock;
@Mock
private Ip ipMock;
private static Date beforeDate;
private static Date afterDate;
private final Long publicIpId = 1L;
private final String dummyIpAddress = "192.168.0.1";
private static final String VLAN_ID_900 = "900"; private static final String VLAN_ID_900 = "900";
private static final String VLAN_ID_901 = "901"; private static final String VLAN_ID_901 = "901";
private static final String VLAN_ID_902 = "902"; private static final String VLAN_ID_902 = "902";
@ -196,6 +245,20 @@ public class NetworkServiceImplTest {
private AutoCloseable closeable; private AutoCloseable closeable;
@BeforeClass
public static void setUpBeforeClass() {
Date date = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DATE, -1);
beforeDate = calendar.getTime();
calendar.setTime(date);
calendar.add(Calendar.DATE, 1);
afterDate = calendar.getTime();
}
private void registerCallContext() { private void registerCallContext() {
account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
account.setId(ACCOUNT_ID); account.setId(ACCOUNT_ID);
@ -231,7 +294,7 @@ public class NetworkServiceImplTest {
service.routerDao = routerDao; service.routerDao = routerDao;
service.commandSetupHelper = commandSetupHelper; service.commandSetupHelper = commandSetupHelper;
service.networkHelper = networkHelper; service.networkHelper = networkHelper;
service._ipAddrMgr = ipAddressManager; service._ipAddrMgr = ipAddressManagerMock;
callContextMocked = Mockito.mockStatic(CallContext.class); callContextMocked = Mockito.mockStatic(CallContext.class);
CallContext callContextMock = Mockito.mock(CallContext.class); CallContext callContextMock = Mockito.mock(CallContext.class);
callContextMocked.when(CallContext::current).thenReturn(callContextMock); callContextMocked.when(CallContext::current).thenReturn(callContextMock);
@ -744,6 +807,113 @@ public class NetworkServiceImplTest {
networkServiceImplMock.validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(1l); networkServiceImplMock.validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(1l);
} }
@Test
public void updatePublicIpAddressInQuarantineTestQuarantineIsAlreadyExpiredShouldThrowCloudRuntimeException() {
Mockito.when(updateQuarantinedIpCmdMock.getId()).thenReturn(publicIpId);
Mockito.when(updateQuarantinedIpCmdMock.getEndDate()).thenReturn(afterDate);
Mockito.when(publicIpQuarantineDaoMock.findById(Mockito.anyLong())).thenReturn(publicIpQuarantineVOMock);
Mockito.when(accountDaoMock.findById(Mockito.anyLong())).thenReturn(accountVOMock);
Mockito.when(domainDaoMock.findById(Mockito.anyLong())).thenReturn(domainVOMock);
Mockito.doNothing().when(accountManager).checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class));
Mockito.when(ipAddressDao.findById(Mockito.anyLong())).thenReturn(ipAddressVOMock);
Mockito.when(ipAddressVOMock.getAddress()).thenReturn(ipMock);
Mockito.when(ipMock.toString()).thenReturn(dummyIpAddress);
Mockito.when(publicIpQuarantineVOMock.getEndDate()).thenReturn(beforeDate);
String expectedMessage = String.format("The quarantine for the public IP address [%s] is no longer active; thus, it cannot be updated.", dummyIpAddress);
CloudRuntimeException assertThrows = Assert.assertThrows(CloudRuntimeException.class,
() -> service.updatePublicIpAddressInQuarantine(updateQuarantinedIpCmdMock));
Assert.assertEquals(expectedMessage, assertThrows.getMessage());
}
@Test
public void updatePublicIpAddressInQuarantineTestGivenEndDateIsBeforeCurrentDateShouldThrowInvalidParameterValueException() {
Mockito.when(updateQuarantinedIpCmdMock.getId()).thenReturn(publicIpId);
Mockito.when(updateQuarantinedIpCmdMock.getEndDate()).thenReturn(beforeDate);
String expectedMessage = String.format("The given end date [%s] is invalid as it is before the current date.", beforeDate);
InvalidParameterValueException assertThrows = Assert.assertThrows(InvalidParameterValueException.class,
() -> service.updatePublicIpAddressInQuarantine(updateQuarantinedIpCmdMock));
Assert.assertEquals(expectedMessage, assertThrows.getMessage());
}
@Test
public void updatePublicIpAddressInQuarantineTestQuarantineIsStillValidAndGivenEndDateIsAfterCurrentDateShouldWork() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(afterDate);
calendar.add(Calendar.DATE, 5);
Date expectedNewEndDate = calendar.getTime();
Mockito.when(updateQuarantinedIpCmdMock.getId()).thenReturn(publicIpId);
Mockito.when(updateQuarantinedIpCmdMock.getEndDate()).thenReturn(expectedNewEndDate);
Mockito.when(publicIpQuarantineDaoMock.findById(Mockito.anyLong())).thenReturn(publicIpQuarantineVOMock);
Mockito.when(accountDaoMock.findById(Mockito.anyLong())).thenReturn(accountVOMock);
Mockito.when(domainDaoMock.findById(Mockito.anyLong())).thenReturn(domainVOMock);
Mockito.doNothing().when(accountManager).checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class));
Mockito.when(ipAddressDao.findById(Mockito.anyLong())).thenReturn(ipAddressVOMock);
Mockito.when(ipAddressDao.findById(Mockito.anyLong())).thenReturn(ipAddressVOMock);
Mockito.when(ipAddressVOMock.getAddress()).thenReturn(ipMock);
Mockito.when(ipMock.toString()).thenReturn(dummyIpAddress);
Mockito.when(publicIpQuarantineVOMock.getEndDate()).thenReturn(afterDate);
Mockito.when(ipAddressManagerMock.updatePublicIpAddressInQuarantine(anyLong(), Mockito.any(Date.class))).thenReturn(publicIpQuarantineVOMock);
PublicIpQuarantine actualPublicIpQuarantine = service.updatePublicIpAddressInQuarantine(updateQuarantinedIpCmdMock);
Mockito.when(actualPublicIpQuarantine.getEndDate()).thenReturn(expectedNewEndDate);
Assert.assertEquals(expectedNewEndDate , actualPublicIpQuarantine.getEndDate());
}
@Test(expected = CloudRuntimeException.class)
public void retrievePublicIpQuarantineTestIpIdNullAndIpAddressNullShouldThrowException() {
service.retrievePublicIpQuarantine(null, null);
}
@Test
public void retrievePublicIpQuarantineTestValidIpIdShouldReturnPublicQuarantine() {
Mockito.when(publicIpQuarantineDaoMock.findById(Mockito.anyLong())).thenReturn(publicIpQuarantineVOMock);
service.retrievePublicIpQuarantine(1L, null);
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(1)).findById(Mockito.anyLong());
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(0)).findByIpAddress(Mockito.anyString());
}
@Test(expected = CloudRuntimeException.class)
public void retrievePublicIpQuarantineTestInvalidIpIdShouldThrowException() {
Mockito.when(publicIpQuarantineDaoMock.findById(Mockito.anyLong())).thenReturn(null);
service.retrievePublicIpQuarantine(1L, null);
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(1)).findById(Mockito.anyLong());
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(0)).findByIpAddress(Mockito.anyString());
}
@Test
public void retrievePublicIpQuarantineTestValidIpAddressShouldReturnPublicQuarantine() {
Mockito.when(publicIpQuarantineDaoMock.findByIpAddress(Mockito.anyString())).thenReturn(publicIpQuarantineVOMock);
service.retrievePublicIpQuarantine(null, "10.1.1.1");
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(0)).findById(Mockito.anyLong());
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(1)).findByIpAddress(Mockito.anyString());
}
@Test(expected = CloudRuntimeException.class)
public void retrievePublicIpQuarantineTestInvalidIpAddressShouldThrowException() {
Mockito.when(publicIpQuarantineDaoMock.findByIpAddress(Mockito.anyString())).thenReturn(null);
service.retrievePublicIpQuarantine(null, "10.1.1.1");
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(0)).findById(Mockito.anyLong());
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(1)).findByIpAddress(Mockito.anyString());
}
@Test
public void retrievePublicIpQuarantineTestIpIdAndAddressInformedShouldUseId() {
Mockito.when(publicIpQuarantineDaoMock.findById(Mockito.anyLong())).thenReturn(publicIpQuarantineVOMock);
service.retrievePublicIpQuarantine(1L, "10.1.1.1");
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(1)).findById(Mockito.anyLong());
Mockito.verify(publicIpQuarantineDaoMock, Mockito.times(0)).findByIpAddress(Mockito.anyString());
}
@Test @Test
public void validateNotSharedNetworkRouterIPv4() { public void validateNotSharedNetworkRouterIPv4() {
NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class); NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
@ -876,7 +1046,7 @@ public class NetworkServiceImplTest {
when(networkVO.getId()).thenReturn(networkId); when(networkVO.getId()).thenReturn(networkId);
when(networkVO.getGuestType()).thenReturn(Network.GuestType.Isolated); when(networkVO.getGuestType()).thenReturn(Network.GuestType.Isolated);
try { try {
when(ipAddressManager.allocateIp(any(), anyBoolean(), any(), anyLong(), any(), any(), eq(srcNatIp))).thenReturn(ipAddress); when(ipAddressManagerMock.allocateIp(any(), anyBoolean(), any(), anyLong(), any(), any(), eq(srcNatIp))).thenReturn(ipAddress);
service.checkAndSetRouterSourceNatIp(account, createNetworkCmd, networkVO); service.checkAndSetRouterSourceNatIp(account, createNetworkCmd, networkVO);
} catch (InsufficientAddressCapacityException | ResourceAllocationException e) { } catch (InsufficientAddressCapacityException | ResourceAllocationException e) {
Assert.fail(e.getMessage()); Assert.fail(e.getMessage());

View File

@ -261,6 +261,11 @@ public class MockUsageEventDao implements UsageEventDao{
return null; return null;
} }
@Override
public UsageEventVO findOneBy(SearchCriteria<UsageEventVO> sc, Filter filter) {
return null;
}
@Override @Override
public Class<UsageEventVO> getEntityBeanType() { public Class<UsageEventVO> getEntityBeanType() {
return null; return null;

View File

@ -16,6 +16,37 @@
// under the License. // under the License.
package com.cloud.vpc; package com.cloud.vpc;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.network.PublicIpQuarantine;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin;
import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd;
import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd;
import org.apache.cloudstack.api.command.admin.network.ListGuestVlansCmd;
import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd;
import org.apache.cloudstack.api.command.user.address.RemoveQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.address.UpdateQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
import org.apache.cloudstack.api.command.user.network.RemoveNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.ResetNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd;
import org.apache.cloudstack.api.command.user.vm.ListNicsCmd;
import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DataCenterDeployment;
import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeployDestination;
import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlan;
@ -66,32 +97,6 @@ import com.cloud.vm.ReservationContext;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachine.Type;
import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfile;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin;
import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd;
import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd;
import org.apache.cloudstack.api.command.admin.network.ListGuestVlansCmd;
import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
import org.apache.cloudstack.api.command.user.network.RemoveNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.ResetNetworkPermissionsCmd;
import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd;
import org.apache.cloudstack.api.command.user.vm.ListNicsCmd;
import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Component @Component
public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrchestrationService, NetworkService { public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrchestrationService, NetworkService {
@ -1052,4 +1057,14 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
@Override @Override
public void validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(final Long serviceOfferingId) { public void validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(final Long serviceOfferingId) {
} }
@Override
public PublicIpQuarantine updatePublicIpAddressInQuarantine(UpdateQuarantinedIpCmd cmd) {
return null;
}
@Override
public void removePublicIpAddressFromQuarantine(RemoveQuarantinedIpCmd cmd) {
}
} }

View File

@ -17,6 +17,31 @@
package org.apache.cloudstack.networkoffering; package org.apache.cloudstack.networkoffering;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import com.cloud.network.dao.PublicIpQuarantineDao;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDao;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.ConfigurationManager;
import com.cloud.event.dao.UsageEventDao; import com.cloud.event.dao.UsageEventDao;
import com.cloud.event.dao.UsageEventDetailsDao; import com.cloud.event.dao.UsageEventDetailsDao;
@ -38,28 +63,6 @@ import com.cloud.user.UserVO;
import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ComponentContext;
import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.UserVmDetailsDao;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDao;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.inject.Inject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/createNetworkOffering.xml") @ContextConfiguration(locations = "classpath:/createNetworkOffering.xml")
@ -101,6 +104,9 @@ public class CreateNetworkOfferingTest extends TestCase {
@Inject @Inject
AnnotationDao annotationDao; AnnotationDao annotationDao;
@Inject
PublicIpQuarantineDao publicIpQuarantineDao;
@Override @Override
@Before @Before
public void setUp() { public void setUp() {

View File

@ -72,4 +72,5 @@
<bean id="PassphraseDaoImpl" class="org.apache.cloudstack.secret.dao.PassphraseDaoImpl" /> <bean id="PassphraseDaoImpl" class="org.apache.cloudstack.secret.dao.PassphraseDaoImpl" />
<bean id="configurationGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl" /> <bean id="configurationGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl" />
<bean id="configurationSubGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl" /> <bean id="configurationSubGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl" />
<bean id="publicIpQuarantineDaoImpl" class="com.cloud.network.dao.PublicIpQuarantineDaoImpl" />
</beans> </beans>

View File

@ -0,0 +1,329 @@
# 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.
import time
from nose.plugins.attrib import attr
from marvin.cloudstackAPI import updateConfiguration
from marvin.cloudstackException import CloudstackAPIException
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.base import Network, NetworkOffering, VpcOffering, VPC, PublicIPAddress
from marvin.lib.common import get_domain, get_zone
class Services:
""" Test Quarantine for public IPs
"""
def __init__(self):
self.services = {
"root_domain": {
"name": "ROOT",
},
"domain_admin": {
"username": "Domain admin",
"roletype": 2,
},
"root_admin": {
"username": "Root admin",
"roletype": 1,
},
"domain_vpc": {
"name": "domain-vpc",
"displaytext": "domain-vpc",
"cidr": "10.1.1.0/24",
},
"domain_network": {
"name": "domain-network",
"displaytext": "domain-network",
},
"root_vpc": {
"name": "root-vpc",
"displaytext": "root-vpc",
"cidr": "10.2.1.0/24",
},
"root_network": {
"name": "root-network",
"displaytext": "root-network",
}
}
class TestQuarantineIPs(cloudstackTestCase):
@classmethod
def setUpClass(cls):
cls.testClient = super(TestQuarantineIPs, cls).getClsTestClient()
cls.apiclient = cls.testClient.getApiClient()
cls.services = Services().services
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
return
def setUp(self):
self.domain_admin_apiclient = self.testClient.getUserApiClient(self.services["domain_admin"]["username"],
self.services["root_domain"]["name"],
self.services["domain_admin"]["roletype"])
self.admin_apiclient = self.testClient.getUserApiClient(self.services["root_admin"]["username"],
self.services["root_domain"]["name"],
self.services["root_admin"]["roletype"])
"""
Set public.ip.address.quarantine.duration to 60 minutes
"""
update_configuration_cmd = updateConfiguration.updateConfigurationCmd()
update_configuration_cmd.name = "public.ip.address.quarantine.duration"
update_configuration_cmd.value = "1"
self.apiclient.updateConfiguration(update_configuration_cmd)
self.cleanup = []
return
def tearDown(self):
"""
Reset public.ip.address.quarantine.duration to 0 minutes
"""
update_configuration_cmd = updateConfiguration.updateConfigurationCmd()
update_configuration_cmd.name = "public.ip.address.quarantine.duration"
update_configuration_cmd.value = "0"
self.apiclient.updateConfiguration(update_configuration_cmd)
super(TestQuarantineIPs, self).tearDown()
def create_vpc(self, api_client, services):
# Get network offering
network_offering = NetworkOffering.list(api_client, name="DefaultIsolatedNetworkOfferingForVpcNetworks")
self.assertTrue(network_offering is not None and len(network_offering) > 0, "No VPC network offering")
# Getting VPC offering
vpc_offering = VpcOffering.list(api_client, name="Default VPC offering")
self.assertTrue(vpc_offering is not None and len(vpc_offering) > 0, "No VPC offerings found")
# Creating VPC
vpc = VPC.create(
apiclient=api_client,
services=services,
networkDomain="vpc.networkacl",
vpcofferingid=vpc_offering[0].id,
zoneid=self.zone.id,
domainid=self.domain.id,
start=False
)
self.cleanup.append(vpc)
self.assertTrue(vpc is not None, "VPC creation failed")
return vpc
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_only_owner_can_allocate_ip_in_quarantine_vpc(self):
""" Test allocate IP in quarantine to VPC.
"""
# Creating Domain Admin VPC
domain_vpc = self.create_vpc(self.domain_admin_apiclient, self.services["domain_vpc"])
# Allocating source nat first
PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
vpcid=domain_vpc.id)
# Getting available public IP address
ip_address = PublicIPAddress.list(self.domain_admin_apiclient, state="Free", listall=True)[0].ipaddress
self.debug(
f"creating public address with zone {self.zone.id} and vpc id {domain_vpc.id} and ip address {ip_address}.")
# Associating public IP address to Domain Admin account
public_ip = PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
vpcid=domain_vpc.id,
ipaddress=ip_address)
self.assertIsNotNone(public_ip, "Failed to Associate IP Address")
self.assertEqual(public_ip.ipaddress.ipaddress, ip_address, "Associated IP is not same as specified")
self.debug(f"Disassociating public IP {public_ip.ipaddress.ipaddress}.")
public_ip.delete(self.domain_admin_apiclient)
# Creating Root Admin VPC
root_vpc = self.create_vpc(self.admin_apiclient, self.services["root_vpc"])
self.debug(f"Trying to allocate the same IP address {ip_address} that is still in quarantine.")
with self.assertRaises(CloudstackAPIException) as exception:
PublicIPAddress.create(self.admin_apiclient,
zoneid=self.zone.id,
vpcid=root_vpc.id,
ipaddress=ip_address)
self.assertIn(f"Failed to allocate public IP address [{ip_address}] as it is in quarantine.",
exception.exception.errorMsg)
# Owner should be able to allocate its IP in quarantine
public_ip = PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
vpcid=domain_vpc.id,
ipaddress=ip_address)
self.assertIsNotNone(public_ip, "Failed to Associate IP Address")
self.assertEqual(public_ip.ipaddress.ipaddress, ip_address, "Associated IP is not same as specified")
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_another_user_can_allocate_ip_after_quarantined_has_ended_vpc(self):
""" Test allocate IP to VPC after quarantine has ended.
"""
# Creating Domain Admin VPC
domain_vpc = self.create_vpc(self.domain_admin_apiclient, self.services["domain_vpc"])
# Allocating source nat first
PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
vpcid=domain_vpc.id)
# Getting available public IP address
ip_address = PublicIPAddress.list(self.domain_admin_apiclient, state="Free", listall=True)[0].ipaddress
self.debug(
f"creating public address with zone {self.zone.id} and vpc id {domain_vpc.id} and ip address {ip_address}.")
# Associating public IP address to Domain Admin account
public_ip = PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
vpcid=domain_vpc.id,
ipaddress=ip_address)
self.assertIsNotNone(public_ip, "Failed to Associate IP Address")
self.assertEqual(public_ip.ipaddress.ipaddress, ip_address, "Associated IP is not same as specified")
self.debug(f"Disassociating public IP {public_ip.ipaddress.ipaddress}.")
public_ip.delete(self.domain_admin_apiclient)
# Creating Root Admin VPC
root_vpc = self.create_vpc(self.admin_apiclient, self.services["root_vpc"])
self.debug(f"Trying to allocate the same IP address {ip_address} after the quarantine duration.")
time.sleep(60)
public_ip_2 = PublicIPAddress.create(self.admin_apiclient,
zoneid=self.zone.id,
vpcid=root_vpc.id,
ipaddress=ip_address)
self.assertIsNotNone(public_ip_2, "Failed to Associate IP Address")
self.assertEqual(public_ip_2.ipaddress.ipaddress, ip_address, "Associated IP is not same as specified")
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_only_owner_can_allocate_ip_in_quarantine_network(self):
""" Test allocate IP in quarantine to network.
"""
network_offering = NetworkOffering.list(self.domain_admin_apiclient,
name="DefaultIsolatedNetworkOfferingWithSourceNatService")
domain_network = Network.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
services=self.services["domain_network"],
networkofferingid=network_offering[0].id)
self.cleanup.append(domain_network)
# Allocating source nat first
PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
networkid=domain_network.id)
# Getting available public IP address
ip_address = PublicIPAddress.list(self.domain_admin_apiclient, state="Free", listall=True)[0].ipaddress
self.debug(
f"creating public address with zone {self.zone.id} and network id {domain_network.id} and ip address {ip_address}.")
# Associating public IP address to Domain Admin account
public_ip = PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
networkid=domain_network.id,
ipaddress=ip_address)
self.assertIsNotNone(public_ip, "Failed to Associate IP Address")
self.assertEqual(public_ip.ipaddress.ipaddress, ip_address, "Associated IP is not same as specified")
self.debug(f"Disassociating public IP {public_ip.ipaddress.ipaddress}.")
public_ip.delete(self.domain_admin_apiclient)
# Creating Root Admin network
root_network = Network.create(self.admin_apiclient,
zoneid=self.zone.id,
services=self.services["root_network"],
networkofferingid=network_offering[0].id)
self.cleanup.append(root_network)
self.debug(f"Trying to allocate the same IP address {ip_address} that is still in quarantine.")
with self.assertRaises(CloudstackAPIException) as exception:
PublicIPAddress.create(self.admin_apiclient,
zoneid=self.zone.id,
networkid=root_network.id,
ipaddress=ip_address)
self.assertIn(f"Failed to allocate public IP address [{ip_address}] as it is in quarantine.",
exception.exception.errorMsg)
# Owner should be able to allocate its IP in quarantine
public_ip = PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
networkid=domain_network.id,
ipaddress=ip_address)
self.assertIsNotNone(public_ip, "Failed to Associate IP Address")
self.assertEqual(public_ip.ipaddress.ipaddress, ip_address, "Associated IP is not same as specified")
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_another_user_can_allocate_ip_after_quarantined_has_ended_network(self):
""" Test allocate IP to network after quarantine has ended.
"""
network_offering = NetworkOffering.list(self.domain_admin_apiclient,
name="DefaultIsolatedNetworkOfferingWithSourceNatService")
domain_network = Network.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
services=self.services["domain_network"],
networkofferingid=network_offering[0].id)
self.cleanup.append(domain_network)
# Allocating source nat first
PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
networkid=domain_network.id)
# Getting available public IP address
ip_address = PublicIPAddress.list(self.domain_admin_apiclient, state="Free", listall=True)[0].ipaddress
self.debug(
f"creating public address with zone {self.zone.id} and network id {domain_network.id} and ip address {ip_address}.")
# Associating public IP address to Domain Admin account
public_ip = PublicIPAddress.create(self.domain_admin_apiclient,
zoneid=self.zone.id,
networkid=domain_network.id,
ipaddress=ip_address)
self.assertIsNotNone(public_ip, "Failed to Associate IP Address")
self.assertEqual(public_ip.ipaddress.ipaddress, ip_address, "Associated IP is not same as specified")
self.debug(f"Disassociating public IP {public_ip.ipaddress.ipaddress}.")
public_ip.delete(self.domain_admin_apiclient)
# Creating Root Admin VPC
root_network = Network.create(self.admin_apiclient,
zoneid=self.zone.id,
services=self.services["root_network"],
networkofferingid=network_offering[0].id)
self.cleanup.append(root_network)
self.debug(f"Trying to allocate the same IP address {ip_address} after the quarantine duration.")
time.sleep(60)
public_ip_2 = PublicIPAddress.create(self.admin_apiclient,
zoneid=self.zone.id,
networkid=domain_network.id,
ipaddress=ip_address)
self.assertIsNotNone(public_ip_2, "Failed to Associate IP Address")
self.assertEqual(public_ip_2.ipaddress.ipaddress, ip_address, "Associated IP is not same as specified")

View File

@ -256,6 +256,9 @@ known_categories = {
'importVsphereStoragePolicies' : 'vSphere storage policies', 'importVsphereStoragePolicies' : 'vSphere storage policies',
'listVsphereStoragePolicies' : 'vSphere storage policies', 'listVsphereStoragePolicies' : 'vSphere storage policies',
'ConsoleEndpoint': 'Console Endpoint', 'ConsoleEndpoint': 'Console Endpoint',
'listQuarantinedIp': 'IP Quarantine',
'updateQuarantinedIp': 'IP Quarantine',
'removeQuarantinedIp': 'IP Quarantine',
'Shutdown': 'Shutdown' 'Shutdown': 'Shutdown'
} }

View File

@ -1854,7 +1854,7 @@ class PublicIPAddress:
if zoneid: if zoneid:
cmd.zoneid = zoneid cmd.zoneid = zoneid
elif "zoneid" in services: elif services and "zoneid" in services:
cmd.zoneid = services["zoneid"] cmd.zoneid = services["zoneid"]
if domainid: if domainid:
@ -5105,7 +5105,7 @@ class VPC:
@classmethod @classmethod
def create(cls, apiclient, services, vpcofferingid, def create(cls, apiclient, services, vpcofferingid,
zoneid, networkDomain=None, account=None, zoneid, networkDomain=None, account=None,
domainid=None, **kwargs): domainid=None, start=True, **kwargs):
"""Creates the virtual private connection (VPC)""" """Creates the virtual private connection (VPC)"""
cmd = createVPC.createVPCCmd() cmd = createVPC.createVPCCmd()
@ -5113,6 +5113,7 @@ class VPC:
cmd.displaytext = "-".join([services["displaytext"], random_gen()]) cmd.displaytext = "-".join([services["displaytext"], random_gen()])
cmd.vpcofferingid = vpcofferingid cmd.vpcofferingid = vpcofferingid
cmd.zoneid = zoneid cmd.zoneid = zoneid
cmd.start = start
if "cidr" in services: if "cidr" in services:
cmd.cidr = services["cidr"] cmd.cidr = services["cidr"]
if account: if account:

View File

@ -453,8 +453,8 @@ export default {
this.onCloseModal() this.onCloseModal()
}).catch(error => { }).catch(error => {
this.$notification.error({ this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`, message: this.$t('message.request.failed'),
description: error.response.data.associateipaddressresponse.errortext || error.response.data.errorresponse.errortext, description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
duration: 0 duration: 0
}) })
}).finally(() => { }).finally(() => {