package io.eguan.vold.rest.resources; /* * #%L * Project eguan * %% * Copyright (C) 2012 - 2017 Oodrive * %% * Licensed 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. * #L% */ import io.eguan.vold.model.DeviceMXBean; import io.eguan.vold.model.SnapshotMXBean; import io.eguan.vold.model.VvrObjectNameFactory; import io.eguan.vold.rest.errors.ClientErrorFactory; import io.eguan.vold.rest.errors.CustomResourceException; import io.eguan.vold.rest.errors.ServerErrorFactory; import io.eguan.vold.rest.generated.model.Device; import io.eguan.vold.rest.generated.model.DeviceList; import io.eguan.vold.rest.generated.model.Snapshot; import io.eguan.vold.rest.generated.model.SnapshotList; import io.eguan.vold.rest.generated.resources.BinarySnapshotResource; import io.eguan.vold.rest.generated.resources.ChildSnapshotsResource; import io.eguan.vold.rest.generated.resources.DescendantDevicesResource; import io.eguan.vold.rest.generated.resources.NewDeviceResource; import io.eguan.vold.rest.generated.resources.SnapshotResource; import io.eguan.vold.rest.generated.resources.UploadSnapshotResource; import io.eguan.vold.rest.util.InputValidation; import java.io.IOException; import java.net.URI; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.UUID; import javax.management.JMX; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.Query; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link SnapshotResource} implementation for JMX backend. * * @author oodrive * @author pwehrle * @author ebredzinski * */ public class SnapshotResourceJmxImpl extends AbstractResource implements SnapshotResource { private static final Logger LOGGER = LoggerFactory.getLogger(SnapshotResourceJmxImpl.class); private final SnapshotMXBean snapshotInstance; private final VvrResourceJmxImpl vvrResource; private final URI resourceUri; public SnapshotResourceJmxImpl(final SnapshotMXBean snapshotProxy, final VvrResourceJmxImpl vvrResource, final URI resourceUri) { this.snapshotInstance = snapshotProxy; this.vvrResource = vvrResource; this.resourceUri = resourceUri; } public final class NewDeviceResourceJmxImpl implements NewDeviceResource { @Override public final Response newDevice(final String ownerId, final String name, final String description, final String uuid, final Long size) { final VvrTasksResourceJmxImpl tasksResource = vvrResource.getVvrTasksResource(); final String taskId; try { if (size == null) { if (uuid == null) { if (description == null) { taskId = snapshotInstance.createDevice(name); } else { taskId = snapshotInstance.createDevice(name, description); } } else { if (description == null) { taskId = snapshotInstance.createDeviceUuid(name, uuid); } else { taskId = snapshotInstance.createDeviceUuid(name, description, uuid); } } } else { if (uuid == null) { if (description == null) { taskId = snapshotInstance.createDevice(name, size); } else { taskId = snapshotInstance.createDevice(name, description, size); } } else { if (description == null) { taskId = snapshotInstance.createDeviceUuid(name, uuid, size); } else { taskId = snapshotInstance.createDeviceUuid(name, description, uuid, size); } } } } catch (final IllegalArgumentException e) { throw ClientErrorFactory.newBadRequestException(e.getMessage(), "Illegal argument for name or uuid", e); } catch (final IllegalStateException e) { throw ClientErrorFactory.newForbiddenException("Invalid size", "Exception create device"); } final URI taskUri = tasksResource.constructTaskUri(taskId); return Response.status(Status.ACCEPTED).location(taskUri).build(); } } public final class UploadSnapshotResourceJmxImpl implements UploadSnapshotResource { @Override public Response uploadSnapshot(final String ownerId, final long size) throws CustomResourceException { // TODO: implement LOGGER.warn("Not implemented"); throw ServerErrorFactory.newNotImplementedException("Not supported by this server", "Snapshot upload not yet implemented"); } } public final class ChildSnapshotsResourceJmxImpl implements ChildSnapshotsResource { @Override public SnapshotList getChildSnapshots(final String ownerId) throws CustomResourceException { final UUID ownerUuid = InputValidation.getUuidFromString(ownerId); final UUID vvrUuid = UUID.fromString(vvrResource.getVvrUuid()); final MBeanServerConnection jmxConnection = vvrResource.getParentResource().getConnection(); final SnapshotList result = getObjectFactory().createSnapshotList(); final List<Snapshot> snapList = result.getSnapshots(); final Set<ObjectName> foundSnapObjNames; try { foundSnapObjNames = jmxConnection.queryNames( VvrObjectNameFactory.newSnapshotQueryListObjectName(ownerUuid, vvrUuid), Query.eq(Query.attr("Parent"), Query.value(snapshotInstance.getUuid()))); } catch (final IOException e) { LOGGER.error("Failed to query server for snapshots", e); throw ServerErrorFactory.newInternalErrorException("Internal communication error", "Exception querying snapshots", e); } for (final ObjectName currObjName : foundSnapObjNames) { snapList.add(SnapshotsResourceJmxImpl.getSnapshotPojoFromMbeanProxy(JMX.newMBeanProxy(jmxConnection, currObjName, SnapshotMXBean.class))); } return result; } } public final class DescendantDevicesResourceJmxImpl implements DescendantDevicesResource { @Override public DeviceList getDescendantDevices(final String ownerId, final Boolean recursive) throws CustomResourceException { final UUID ownerUuid = InputValidation.getUuidFromString(ownerId); final UUID vvrUuid = UUID.fromString(vvrResource.getVvrUuid()); final MBeanServerConnection jmxConnection = vvrResource.getParentResource().getConnection(); final DeviceList result = getObjectFactory().createDeviceList(); final List<Device> devList = result.getDevices(); final Set<ObjectName> foundDevObjNames; try { if (recursive) { LOGGER.warn("Recursive search for device not implemented"); throw ServerErrorFactory.newNotImplementedException("Not supported by this server", "Recursive search for device not implemented"); } else { foundDevObjNames = jmxConnection.queryNames( VvrObjectNameFactory.newDeviceQueryListObjectName(ownerUuid, vvrUuid), Query.eq(Query.attr("Parent"), Query.value(snapshotInstance.getUuid()))); } } catch (final IOException e) { LOGGER.error("Failed to query server for devices", e); throw ServerErrorFactory.newInternalErrorException("Internal communication error", "Exception querying Devices", e); } for (final ObjectName currObjName : foundDevObjNames) { devList.add(DevicesResourceJmxImpl.getDevicePojoFromMbeanProxy(JMX.newMBeanProxy(jmxConnection, currObjName, DeviceMXBean.class))); } return result; } } public final class BinarySnapshotResourceJmxImpl implements BinarySnapshotResource { @Override public Response getBinarySnapshot(final String ownerId) throws CustomResourceException { // TODO: implement LOGGER.warn("Not implemented"); throw ServerErrorFactory.newNotImplementedException("Not supported by this server", "Binary snapshot access not yet implemented"); } } @Override public final Snapshot getSnapshot(final String ownerId) { return SnapshotsResourceJmxImpl.getSnapshotPojoFromMbeanProxy(snapshotInstance); } @Override public final Snapshot postSnapshot(final String ownerId, final Snapshot snapshot) throws CustomResourceException { Objects.requireNonNull(snapshot); if (LOGGER.isDebugEnabled()) { LOGGER.debug("POST on Snapshot " + snapshotInstance.getUuid() + "; argument=" + snapshot.toString()); } boolean readOnlyChanged = false; readOnlyChanged |= !snapshotInstance.getUuid().equals(snapshot.getUuid()); readOnlyChanged |= !snapshotInstance.getParent().equals(snapshot.getParent()); readOnlyChanged |= (snapshotInstance.getSize() != snapshot.getSize()); if (readOnlyChanged) { LOGGER.warn("Detected read-only attribute change on Snapshot " + snapshotInstance.getUuid()); throw ClientErrorFactory.newForbiddenException("Write on read-only attributes forbidden", "Tried to modify read-only attributes for snapshot " + snapshotInstance.getUuid()); } snapshotInstance.setName(snapshot.getName()); snapshotInstance.setDescription(snapshot.getDescription()); return SnapshotsResourceJmxImpl.getSnapshotPojoFromMbeanProxy(snapshotInstance); } @Override public Response deleteSnapshot(final String ownerId) { final VvrTasksResourceJmxImpl tasksResource = vvrResource.getVvrTasksResource(); final String taskId; try { taskId = snapshotInstance.delete(); } catch (final Exception e) { throw ServerErrorFactory.newInternalErrorException("Failed to delete Snapshot", "Exception delete snapshot", e); } final URI taskUri = tasksResource.constructTaskUri(taskId); // FIXME delete root snapshoot (with its ID not with /root) does not return 403 return Response.status(Status.ACCEPTED).location(taskUri).build(); } @Override public final NewDeviceResource getNewDeviceResource(final String ownerId) { return new NewDeviceResourceJmxImpl(); } @Override public final UploadSnapshotResource getUploadSnapshotResource(final String ownerId) { return new UploadSnapshotResourceJmxImpl(); } @Override public final ChildSnapshotsResource getChildSnapshotsResource(final String ownerId) { return new ChildSnapshotsResourceJmxImpl(); } @Override public final DescendantDevicesResource getDescendantDevicesResource(final String ownerId) { return new DescendantDevicesResourceJmxImpl(); } @Override public final BinarySnapshotResource getBinarySnapshotResource(final String ownerId) { return new BinarySnapshotResourceJmxImpl(); } @Override protected URI getResourceUri() { return resourceUri; } }