/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource.cinder; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.emc.storageos.api.service.authorization.PermissionsHelper; import com.emc.storageos.api.service.impl.resource.TaskResourceService; import com.emc.storageos.api.service.impl.resource.utils.CinderApiUtils; import com.emc.storageos.api.service.impl.response.ProjOwnedResRepFilter; import com.emc.storageos.api.service.impl.response.ResRepFilter; import com.emc.storageos.cinder.CinderConstants; import com.emc.storageos.cinder.model.CinderAvailZonesResp; import com.emc.storageos.cinder.model.CinderAvailabiltyZone; import com.emc.storageos.cinder.model.CinderExtension; import com.emc.storageos.cinder.model.CinderExtensionsRestResp; import com.emc.storageos.cinder.model.CinderLimits; import com.emc.storageos.cinder.model.CinderLimitsDetail; import com.emc.storageos.cinder.model.CinderOsServicesRestResp; import com.emc.storageos.cinder.model.CinderOsService; import com.emc.storageos.cinder.model.CinderOsVolumeTransferRestResp; import com.emc.storageos.cinder.model.UsageStats; import com.emc.storageos.coordinator.client.model.Constants; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.coordinator.common.Service; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.QuotaOfCinder; import com.emc.storageos.db.client.model.VirtualArray; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.model.RelatedResourceRep; import com.emc.storageos.model.ResourceTypeEnum; import com.emc.storageos.security.authentication.StorageOSUser; import com.emc.storageos.security.authorization.ACL; import com.emc.storageos.security.authorization.CheckPermission; import com.emc.storageos.security.authorization.DefaultPermissions; import com.emc.storageos.security.authorization.Role; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.emc.storageos.db.client.model.StorageSystem; import java.util.Iterator; import java.util.Date; import java.text.SimpleDateFormat; @Path("/v2/{tenant_id}") @DefaultPermissions(readRoles = { Role.TENANT_ADMIN, Role.SYSTEM_MONITOR }, readAcls = { ACL.USE }, writeRoles = {}) @SuppressWarnings({ "unchecked", "rawtypes" }) public class MiscService extends TaskResourceService { private static final Logger _log = LoggerFactory.getLogger(MiscService.class); private CinderHelpers helper = null; @Autowired private CoordinatorClient _coordinator; @Override public Class<CinderLimitsDetail> getResourceClass() { return CinderLimitsDetail.class; } private CinderHelpers getCinderHelper() { return CinderHelpers.getInstance(_dbClient, _permissionsHelper); } private QuotaHelper getQuotaHelper() { return QuotaHelper.getInstance(_dbClient, _permissionsHelper); } /** * Get Limits * * @prereq none * @param tenant_id the URN of the tenant * @brief Get Limits * @return limits */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/limits") @CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, acls = { ACL.ANY }) public Response getLimits(@PathParam("tenant_id") String openstack_tenant_id, @Context HttpHeaders header) { _log.info("START get limits"); CinderLimits limitsResp = new CinderLimits(); Project project = getCinderHelper().getProject(openstack_tenant_id.toString(), getUserFromContext()); if (project == null) { throw APIException.badRequests.projectWithTagNonexistent(openstack_tenant_id); } HashMap<String, String> defaultQuotaMap = getQuotaHelper().loadDefaultsMapFromDb(); int totalSizeUsed = 0; int maxQuota = Long.valueOf(defaultQuotaMap.get(CinderConstants.ResourceQuotaDefaults.GIGABYTES.getResource())).intValue(); int maxTotalVolumes = 0; int maxTotalSnapshots = 0; int totalVolumesUsed = 0; int totalSnapshotsUsed = 0; if (project.getQuotaEnabled()) { maxQuota = (int) (project.getQuota().intValue()); } UsageStats objUsageStats = new UsageStats(); objUsageStats = getQuotaHelper().getStorageStats(null, project.getId()); totalVolumesUsed = (int) objUsageStats.volumes; totalSnapshotsUsed = (int) objUsageStats.snapshots; totalSizeUsed = (int) objUsageStats.spaceUsed; QuotaOfCinder projQuota = getQuotaHelper().getProjectQuota(openstack_tenant_id, getUserFromContext()); if (projQuota != null) { maxTotalVolumes = projQuota.getVolumesLimit().intValue(); maxTotalSnapshots = (int) projQuota.getSnapshotsLimit().intValue(); } else { QuotaOfCinder quotaObj = new QuotaOfCinder(); quotaObj.setId(URI.create(UUID.randomUUID().toString())); quotaObj.setProject(project.getId()); quotaObj.setVolumesLimit(Long.valueOf(defaultQuotaMap.get(CinderConstants.ResourceQuotaDefaults.VOLUMES.getResource()))); quotaObj.setSnapshotsLimit(Long.valueOf(defaultQuotaMap.get(CinderConstants.ResourceQuotaDefaults.SNAPSHOTS.getResource()))); quotaObj.setTotalQuota((long) maxQuota); _dbClient.createObject(quotaObj); maxTotalSnapshots = quotaObj.getSnapshotsLimit().intValue(); maxTotalVolumes = quotaObj.getVolumesLimit().intValue(); } Map<String, Integer> absoluteDetailsMap = new HashMap<String, Integer>(); absoluteDetailsMap.put("totalSnapshotsUsed", totalSnapshotsUsed); absoluteDetailsMap.put("maxTotalVolumeGigabytes", maxQuota); absoluteDetailsMap.put("totalGigabytesUsed", totalSizeUsed); absoluteDetailsMap.put("maxTotalSnapshots", maxTotalSnapshots); absoluteDetailsMap.put("totalVolumesUsed", totalVolumesUsed); absoluteDetailsMap.put("maxTotalVolumes", maxTotalVolumes); limitsResp.absolute = absoluteDetailsMap; _log.info("END get limits"); return CinderApiUtils.getCinderResponse(limitsResp, header, true, CinderConstants.STATUS_OK); } /** * Get extensions * * @prereq none * @param tenant_id the URN of the tenant * @brief Get extensions * @return extensions * NOTE: Horizon invokes GET request on /extensions URI. * This function is implemented, so as to not break the horizon UI. */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/extensions") @CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, acls = { ACL.ANY }) public Response getExtensions(@PathParam("tenant_id") String openstack_tenant_id, @Context HttpHeaders header) { // Here we ignore the openstack tenant id _log.info("START get extensions"); CinderExtensionsRestResp extResp = new CinderExtensionsRestResp(); CinderExtension hostExt = new CinderExtension(); CinderExtension objExt = new CinderExtension(); objExt.alias = "os-availability-zone"; objExt.description = "Describe Availability Zones."; objExt.namespace = "http://docs.openstack.org/volume/ext/os-availability-zone/api/v1"; // TODO what do we do about the below hard coding? objExt.updated = "2013-06-27T00:00:00+00:00"; objExt.name = "AvailabilityZones"; extResp.getExtensions().add(objExt); hostExt.alias = "os-hosts"; hostExt.description = "Admin-only host administration."; hostExt.namespace = "http://docs.openstack.org/volume/ext/hosts/api/v1.1"; hostExt.updated = "2011-06-29T00:00:00+00:00"; hostExt.name = "Hosts"; extResp.getExtensions().add(hostExt); _log.info("END get extensions"); return CinderApiUtils.getCinderResponse(extResp, header, false, CinderConstants.STATUS_OK); } /** * Get os-volume-transfer details * NOTE: This dummy function has been implemented so that it does not break the horizon. * We are not returning an unsupported exception, as horizon invokes this call, when we * click on volumes tab. Hence, we implement the dummy. * @prereq none * @param tenant_id the URN of the tenant * @return transfers */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/os-volume-transfer/detail") @CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, acls = { ACL.ANY }) public Response getVolumeTransfers(@PathParam("tenant_id") String openstack_tenant_id, @Context HttpHeaders header) { _log.info("START getVolumeTransfers"); CinderOsVolumeTransferRestResp volTransferResp = new CinderOsVolumeTransferRestResp(); return CinderApiUtils.getCinderResponse(volTransferResp, header, false,CinderConstants.STATUS_OK); } /** * Get os-services * This function returns the cinder services and their details. * need to support system Information os-service * @prereq none * @param tenant_id the URN of the tenant * @return services */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/os-services") @CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, acls = { ACL.ANY }) public Response getOsServices(@PathParam("tenant_id") String openstack_tenant_id, @Context HttpHeaders header) { _log.info("START getOsServices"); boolean serviceDown = false; CinderOsServicesRestResp osServicesResp = new CinderOsServicesRestResp(); CinderOsService volumeService = new CinderOsService(); CinderOsService schedulerService = new CinderOsService(); Date curDate = new Date(); SimpleDateFormat format = new SimpleDateFormat("dd-M-yyyy hh:mm:ss:SSS"); volumeService.setBinary("coprHD-volume"); volumeService.setZone("nova"); schedulerService.setBinary("coprHD-scheduler"); schedulerService.setZone("nova"); if (_coordinator == null || !_coordinator.isConnected()) { _log.info("Coordinator service is Down"); serviceDown = true; } else { if((_coordinator.locateAllSvcsAllVers("geosvc").isEmpty()) || (_coordinator.locateAllSvcsAllVers("geodbsvc").isEmpty()) || (_coordinator.locateAllSvcsAllVers("dbsvc").isEmpty()) || (_coordinator.locateAllSvcsAllVers("authsvc").isEmpty()) || (_coordinator.locateAllSvcsAllVers("syssvc").isEmpty()) || (_coordinator.locateAllSvcsAllVers("controllersvc").isEmpty())) { _log.info("Services like geosvc/geodbsvc/dbsvc/authsvc/syssvc/controllersvc may be Down"); serviceDown = true; } } if (serviceDown) { volumeService.setHost(""); volumeService.setState("down"); volumeService.setStatus("disabled"); volumeService.setDisabledReason("Required coprHD service or services may be down"); curDate = new Date(); volumeService.setUpdatedAt(format.format(curDate)); osServicesResp.getServices().add(volumeService); schedulerService.setState("down"); schedulerService.setStatus("disabled"); schedulerService.setDisabledReason("Required coprHD service or services may be down"); schedulerService.setHost(""); curDate = new Date(); schedulerService.setUpdatedAt(format.format(curDate)); osServicesResp.getServices().add(schedulerService); return CinderApiUtils.getCinderResponse(osServicesResp, header, false,CinderConstants.STATUS_OK); } String localNodeId = _coordinator.getInetAddessLookupMap().getNodeId(); volumeService.setHost(localNodeId); //If Apisvc is running and any storage system is registered then coprHD-volume is up List<URI> ids = _dbClient.queryByType(StorageSystem.class, true); Iterator<StorageSystem> iter = _dbClient.queryIterativeObjects(StorageSystem.class, ids); if (iter.hasNext()) { volumeService.setState("up"); volumeService.setStatus("enabled"); volumeService.setDisabledReason(null); } else { volumeService.setState("down"); volumeService.setStatus("disabled"); volumeService.setDisabledReason("No storage system is discovered"); } curDate = new Date(); volumeService.setUpdatedAt(format.format(curDate)); osServicesResp.getServices().add(volumeService); schedulerService.setHost(localNodeId); schedulerService.setState("up"); schedulerService.setStatus("enabled"); schedulerService.setDisabledReason(null); curDate = new Date(); schedulerService.setUpdatedAt(format.format(curDate)); osServicesResp.getServices().add(schedulerService); return CinderApiUtils.getCinderResponse(osServicesResp, header, false,CinderConstants.STATUS_OK); } /** * Get availability zones. * NOTE:The availability zone in Openstack maps to the virtual array in the ViPR * * @prereq none * @param tenant_id the URN of the tenant * @brief Get availability zones * @return availability zones */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/os-availability-zone") @CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, acls = { ACL.ANY }) public CinderAvailZonesResp getAvailabilityZones(@PathParam("tenant_id") String openstack_tenant_id) { // Here we ignore the openstack tenant id _log.info("START get availability zones"); CinderAvailZonesResp objCinderAvailZonesResp = new CinderAvailZonesResp(); List<CinderAvailabiltyZone> az_list = objCinderAvailZonesResp.getAvailabilityZoneInfo(); _log.debug("retrieving virtual arrays via dbclient"); getCinderHelper().getAvailabilityZones(az_list, getUserFromContext()); return objCinderAvailZonesResp; } private CinderAvailabiltyZone extractAvailabilityZone(VirtualArray nh) { CinderAvailabiltyZone objAz = new CinderAvailabiltyZone(); objAz.zoneName = nh.getLabel(); objAz.zoneState.available = !nh.getInactive(); return objAz; } /** * Get object specific permissions filter * */ @Override protected ResRepFilter<? extends RelatedResourceRep> getPermissionFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { return new ProjOwnedResRepFilter(user, permissionsHelper, VirtualPool.class); } @Override protected DataObject queryResource(URI id) { throw new UnsupportedOperationException(); } @Override protected URI getTenantOwner(URI id) { throw new UnsupportedOperationException(); } @Override protected ResourceTypeEnum getResourceType() { throw new UnsupportedOperationException(); } }