package org.zstack.network.service.virtualrouter; import org.springframework.beans.factory.annotation.Autowired; import org.zstack.core.CoreGlobalProperty; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.header.apimediator.ApiMessageInterceptionException; import org.zstack.header.apimediator.ApiMessageInterceptor; import org.zstack.header.identity.IdentityErrors; import org.zstack.header.image.ImageConstant; import org.zstack.header.image.ImageConstant.ImageMediaType; import org.zstack.header.image.ImageVO; import org.zstack.header.image.ImageVO_; import org.zstack.header.message.APIMessage; import org.zstack.header.network.l3.IpRangeVO; import org.zstack.header.network.l3.IpRangeVO_; import org.zstack.header.network.l3.L3NetworkVO; import org.zstack.header.network.l3.L3NetworkVO_; import org.zstack.header.network.service.NetworkServiceL3NetworkRefVO; import org.zstack.header.network.service.NetworkServiceL3NetworkRefVO_; import org.zstack.header.network.service.NetworkServiceType; import org.zstack.header.query.QueryCondition; import org.zstack.header.query.QueryOp; import org.zstack.identity.QuotaUtil; import org.zstack.utils.ShellResult; import org.zstack.utils.ShellUtils; import org.zstack.utils.network.NetworkUtils; import static org.zstack.core.Platform.argerr; import static org.zstack.core.Platform.operr; import javax.persistence.Tuple; import java.util.List; import static org.zstack.utils.CollectionDSL.list; /** */ public class VirtualRouterApiInterceptor implements ApiMessageInterceptor { @Autowired private DatabaseFacade dbf; @Autowired private ErrorFacade errf; @Override public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException { if (msg instanceof APIQueryVirtualRouterOfferingMsg) { validate((APIQueryVirtualRouterOfferingMsg) msg); } else if (msg instanceof APICreateVirtualRouterOfferingMsg) { validate((APICreateVirtualRouterOfferingMsg) msg); } else if (msg instanceof APIUpdateVirtualRouterOfferingMsg) { validate((APIUpdateVirtualRouterOfferingMsg) msg); } return msg; } private void validate(APIUpdateVirtualRouterOfferingMsg msg) { if (msg.getIsDefault() != null) { if (!new QuotaUtil().isAdminAccount(msg.getSession().getAccountUuid())) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED, "cannot change the default field of a virtual router offering; only admin can do the operation" )); } } if (msg.getImageUuid() != null) { SimpleQuery<ImageVO> q = dbf.createQuery(ImageVO.class); q.select(ImageVO_.mediaType, ImageVO_.format); q.add(ImageVO_.uuid, Op.EQ, msg.getImageUuid()); Tuple t = q.findTuple(); ImageMediaType type = t.get(0, ImageMediaType.class); String format = t.get(1, String.class); if (type != ImageMediaType.RootVolumeTemplate) { throw new ApiMessageInterceptionException(argerr("image[uuid:%s]'s mediaType is %s, the mediaType of a virtual router image must be %s", msg.getImageUuid(), type, ImageMediaType.RootVolumeTemplate)); } if (ImageConstant.ISO_FORMAT_STRING.equals(format)) { throw new ApiMessageInterceptionException(argerr("image[uuid:%s] is of format %s, cannot be used for virtual router", msg.getImageUuid(), format)); } } } private void validate(APICreateVirtualRouterOfferingMsg msg) { if (msg.isDefault() != null) { if (!new QuotaUtil().isAdminAccount(msg.getSession().getAccountUuid())) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.PERMISSION_DENIED, "cannot create a virtual router offering with the default field set; only admin can do the operation" )); } } if (msg.getPublicNetworkUuid() == null) { msg.setPublicNetworkUuid(msg.getManagementNetworkUuid()); } SimpleQuery<L3NetworkVO> q = dbf.createQuery(L3NetworkVO.class); q.select(L3NetworkVO_.zoneUuid); q.add(L3NetworkVO_.uuid, Op.EQ, msg.getManagementNetworkUuid()); String zoneUuid = q.findValue(); if (!zoneUuid.equals(msg.getZoneUuid())) { throw new ApiMessageInterceptionException(argerr("management network[uuid:%s] is not in the same zone[uuid:%s] this offering is going to create", msg.getManagementNetworkUuid(), msg.getZoneUuid())); } if (!CoreGlobalProperty.UNIT_TEST_ON) { checkIfManagementNetworkReachable(msg.getManagementNetworkUuid()); } q = dbf.createQuery(L3NetworkVO.class); q.select(L3NetworkVO_.zoneUuid); q.add(L3NetworkVO_.uuid, Op.EQ, msg.getPublicNetworkUuid()); zoneUuid = q.findValue(); if (!zoneUuid.equals(msg.getZoneUuid())) { throw new ApiMessageInterceptionException(argerr("public network[uuid:%s] is not in the same zone[uuid:%s] this offering is going to create", msg.getManagementNetworkUuid(), msg.getZoneUuid())); } SimpleQuery<ImageVO> imq = dbf.createQuery(ImageVO.class); imq.select(ImageVO_.mediaType, ImageVO_.format); imq.add(ImageVO_.uuid, Op.EQ, msg.getImageUuid()); Tuple t = imq.findTuple(); ImageMediaType type = t.get(0, ImageMediaType.class); if (type != ImageMediaType.RootVolumeTemplate) { throw new ApiMessageInterceptionException(argerr("image[uuid:%s]'s mediaType is %s, the mediaType of a virtual router image must be %s", msg.getImageUuid(), type, ImageMediaType.RootVolumeTemplate)); } String format = t.get(1, String.class); if (ImageConstant.ISO_FORMAT_STRING.equals(format)) { throw new ApiMessageInterceptionException(argerr("image[uuid:%s] is of format %s, cannot be used for virtual router", msg.getImageUuid(), format)); } SimpleQuery<NetworkServiceL3NetworkRefVO> nq = dbf.createQuery(NetworkServiceL3NetworkRefVO.class); nq.add(NetworkServiceL3NetworkRefVO_.l3NetworkUuid, Op.IN, list(msg.getPublicNetworkUuid(), msg.getManagementNetworkUuid())); List<NetworkServiceL3NetworkRefVO> nrefs= nq.list(); for (NetworkServiceL3NetworkRefVO nref : nrefs) { if (NetworkServiceType.SNAT.toString().equals(nref.getNetworkServiceType())) { if (nref.getL3NetworkUuid().equals(msg.getManagementNetworkUuid())) { throw new ApiMessageInterceptionException(argerr("the L3 network[uuid: %s] has the SNAT service enabled, it cannot be used as a management network", msg.getManagementNetworkUuid())); } else if (nref.getL3NetworkUuid().equals(msg.getPublicNetworkUuid())) { throw new ApiMessageInterceptionException(argerr("the L3 network[uuid: %s] has the SNAT service enabled, it cannot be used as a public network", msg.getPublicNetworkUuid())); } } } } private void checkIfManagementNetworkReachable(String managementNetworkUuid) { SimpleQuery<IpRangeVO> q = dbf.createQuery(IpRangeVO.class); q.add(IpRangeVO_.l3NetworkUuid, Op.EQ, managementNetworkUuid); List<IpRangeVO> iprs = q.list(); if (iprs.isEmpty()) { throw new ApiMessageInterceptionException(operr("the management network[uuid:%s] doesn't have any IP range", managementNetworkUuid)); } String startIp = iprs.get(0).getStartIp(); if (!NetworkUtils.isIpRoutedByDefaultGateway(startIp)) { // the mgmt server is in the same subnet of the mgmt network return; } String gateway = iprs.get(0).getGateway(); for (int i=0; i<3; i++) { ShellResult ret = ShellUtils.runAndReturn(String.format("ping -c 1 -W 2 %s", gateway)); if (ret.isReturnCode(0)) { return; } } throw new ApiMessageInterceptionException(argerr("the management network[uuid:%s, gateway:%s] is not reachable", managementNetworkUuid, gateway)); } private void validate(APIQueryVirtualRouterOfferingMsg msg) { boolean found = false; for (QueryCondition qcond : msg.getConditions()) { if ("type".equals(qcond.getName())) { qcond.setOp(QueryOp.EQ.toString()); qcond.setValue(VirtualRouterConstant.VIRTUAL_ROUTER_OFFERING_TYPE); found = true; break; } } if (!found) { msg.addQueryCondition("type", QueryOp.EQ, VirtualRouterConstant.VIRTUAL_ROUTER_OFFERING_TYPE); } } }