package org.dcache.restful.qos; import org.json.JSONException; import org.json.JSONObject; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Path; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Produces; import javax.ws.rs.Consumes; import javax.ws.rs.PathParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Context; import javax.ws.rs.InternalServerErrorException; import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.NotFoundException; import javax.ws.rs.ForbiddenException; import javax.ws.rs.BadRequestException; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.util.EnumSet; import java.util.Set; import org.dcache.pinmanager.PinManagerPinMessage; import org.dcache.pinmanager.PinManagerUnpinMessage; import org.dcache.pinmanager.PinManagerCountPinsMessage; import org.dcache.auth.Subjects; import org.dcache.namespace.FileAttribute; import org.dcache.restful.util.ServletContextHandlerAttributes; import org.dcache.vehicles.FileAttributes; import diskCacheV111.util.CacheException; import diskCacheV111.util.FileLocality; import diskCacheV111.util.FileNotFoundCacheException; import diskCacheV111.util.FsPath; import diskCacheV111.util.PermissionDeniedCacheException; import diskCacheV111.util.PnfsHandler; import org.dcache.cells.CellStub; import org.dcache.poolmanager.RemotePoolMonitor; import diskCacheV111.vehicles.HttpProtocolInfo; import dmg.cells.nucleus.NoRouteToCellException; /** * Query current QoS for a file or change the current QoS */ @Path("/qos-management/namespace") public class QosManagementNamespace { @Context ServletContext ctx; @Context HttpServletRequest request; /** ID provided by the requestor eg. the SRM door or QoS.**/ private final String requestId= "qos"; /** * Gets the current status of the object, (including transition status), for the object specified by path. * * @param requestPath path to a file * @return JSONObject current QoS status * @throws CacheException */ @GET @Path("{requestPath : .*}") @Produces(MediaType.APPLICATION_JSON) public BackendCapabilityResponse getQosStatus(@PathParam("requestPath") String requestPath) throws CacheException, URISyntaxException { BackendCapabilityResponse response = new BackendCapabilityResponse(); try { if (Subjects.isNobody(ServletContextHandlerAttributes.getSubject())) { throw new PermissionDeniedCacheException("Permission denied"); } FileAttributes fileAttributes = getFileAttributes(requestPath); FileLocality fileLocality = getLocality(fileAttributes); CellStub cellStub = ServletContextHandlerAttributes.getPinManager(ctx); boolean isPinned = isPinned(fileAttributes, cellStub); switch (fileLocality) { case NEARLINE: if (isPinned) { response.setQoS(QosManagement.TAPE); response.setTargetQoS(QosManagement.DISK_TAPE); } else { response.setQoS(QosManagement.TAPE); } break; case ONLINE: response.setQoS(QosManagement.DISK); break; case ONLINE_AND_NEARLINE: /* When the locality of the file is NEARLINE_ONLINE and * the object is not pinned the result for the user will be displayed as NEARLINE (Tape). * else nearline_online (disk+tape) */ if (isPinned) { response.setQoS(QosManagement.DISK_TAPE); } else { response.setQoS(QosManagement.TAPE); } break; default: // error cases throw new InternalServerErrorException(); } } catch (PermissionDeniedCacheException e) { if (Subjects.isNobody(ServletContextHandlerAttributes.getSubject())) { throw new NotAuthorizedException(e); } else { throw new ForbiddenException(e); } } catch (FileNotFoundCacheException e) { throw new NotFoundException(e); } catch (CacheException | NoRouteToCellException | InterruptedException e) { throw new InternalServerErrorException(e); } return response; } /** * Starts a object transition to the specified QoS. * * @param requestPath path to a file * @param requestPath requestQuery * @return JSONObject current QoS status * @throws CacheException */ @POST @Path("{requestPath : .*}") @Consumes({MediaType.APPLICATION_JSON}) @Produces(MediaType.APPLICATION_JSON) public String changeQosStatus(@PathParam("requestPath") String requestPath, String requestQuery) throws CacheException, URISyntaxException, InterruptedException { JSONObject jsonResponse = new JSONObject(); try { if (Subjects.isNobody(ServletContextHandlerAttributes.getSubject())) { throw new PermissionDeniedCacheException("Permission denied"); } JSONObject jsonRequest = new JSONObject(requestQuery); String update = jsonRequest.get("update").toString(); FileAttributes fileAttributes = getFileAttributes(requestPath); CellStub cellStub = ServletContextHandlerAttributes.getPinManager(ctx); FileLocality fileLocality = getLocality(fileAttributes); switch (update) { // change QoS to "disk+tape" case QosManagement.DISK_TAPE: makeDiskAndTape( fileLocality, fileAttributes, cellStub); break; // change QoS to "tape" case QosManagement.TAPE: makeTape( fileLocality, fileAttributes, cellStub); break; default: // error cases throw new BadRequestException(); } } catch (PermissionDeniedCacheException e) { if (Subjects.isNobody(ServletContextHandlerAttributes.getSubject())) { throw new NotAuthorizedException(e); } else { throw new ForbiddenException(e); } } catch (FileNotFoundCacheException e) { throw new NotFoundException(e); } catch (CacheException e) { throw new InternalServerErrorException(e); } catch (JSONException e) { throw new BadRequestException(e); } jsonResponse.put("status", "200"); jsonResponse.put("message", "Transition was successful"); return jsonResponse.toString(); } public FileAttributes getFileAttributes(String requestPath) throws CacheException { PnfsHandler handler = ServletContextHandlerAttributes.getPnfsHandler(ctx); FsPath path; if (requestPath == null || requestPath.isEmpty()) { path = FsPath.ROOT; } else { path = FsPath.create(FsPath.ROOT + requestPath); } Set<FileAttribute> attributes = EnumSet.allOf(FileAttribute.class); FileAttributes namespaceAttrributes = handler.getFileAttributes(path, attributes); return namespaceAttrributes; } /* * While pinning files this requestId value "qos" will be stored for the pin. * Storing requestId insures the possibility to filter files being pinned by Qos or SRM. */ public void pin(FileAttributes fileAttributes, CellStub cellStub) throws URISyntaxException { HttpProtocolInfo protocolInfo = new HttpProtocolInfo("Http", 1, 1, new InetSocketAddress(request.getRemoteHost(), 0), null, null, null, new URI("http", request.getRemoteHost(), null, null)); PinManagerPinMessage message = new PinManagerPinMessage(fileAttributes, protocolInfo, requestId, -1); cellStub.notify(message); } public FileLocality getLocality(FileAttributes fileAttributes) { RemotePoolMonitor remotePoolMonitor = ServletContextHandlerAttributes.getRemotePoolMonitor(ctx); return remotePoolMonitor.getFileLocality(fileAttributes, request.getRemoteHost()); } public boolean isPinned(FileAttributes fileAttributes, CellStub cellStub) throws CacheException, InterruptedException, URISyntaxException, NoRouteToCellException { boolean isPinned = false; PinManagerCountPinsMessage message = new PinManagerCountPinsMessage(fileAttributes.getPnfsId()); message = cellStub.sendAndWait(message); if (message.getCount() != 0 ){ isPinned = true; } return isPinned; } public void unpin(FileAttributes fileAttributes, CellStub cellStub ) { PinManagerUnpinMessage message = new PinManagerUnpinMessage(fileAttributes.getPnfsId()); message.setRequestId(requestId); cellStub.notify(message); } public void makeDiskAndTape(FileLocality fileLocality, FileAttributes fileAttributes, CellStub cellStub) throws URISyntaxException, CacheException, InterruptedException { switch (fileLocality) { case NEARLINE: pin(fileAttributes, cellStub); break; case ONLINE_AND_NEARLINE: pin(fileAttributes, cellStub); break; default: // error cases throw new BadRequestException(); } } public void makeTape(FileLocality fileLocality, FileAttributes fileAttributes, CellStub cellStub) { switch (fileLocality) { case NEARLINE: unpin(fileAttributes, cellStub); break; case ONLINE_AND_NEARLINE: unpin(fileAttributes, cellStub); break; default: // error cases throw new BadRequestException(); } } }