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.jmx.client.JmxClientConnectionFactory; import io.eguan.vold.model.VvrMXBean; import io.eguan.vold.model.VvrManagementException; import io.eguan.vold.model.VvrManagerMXBean; import io.eguan.vold.model.VvrObjectNameFactory; import io.eguan.vold.rest.errors.CustomResourceException; import io.eguan.vold.rest.errors.ServerErrorFactory; import io.eguan.vold.rest.generated.model.VersionedVolumeRepository; import io.eguan.vold.rest.generated.model.VersionedVolumeRepositoryList; import io.eguan.vold.rest.generated.resources.CreateVvrResource; import io.eguan.vold.rest.generated.resources.VvrResource; import io.eguan.vold.rest.generated.resources.VvrsResource; import io.eguan.vold.rest.generated.resources.VvrsTasksResource; import io.eguan.vold.rest.util.InputValidation; import io.eguan.vold.rest.util.ResourcePath; import java.io.IOException; import java.net.URI; import java.util.List; 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 javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Strings; /** * {@link VvrsResource} implementation for JMX backend. * * @author oodrive * @author pwehrle * @author ebredzinski * */ public final class VvrsResourceJmxImpl extends AbstractResource implements VvrsResource { private static final Logger LOGGER = LoggerFactory.getLogger(VvrsResourceJmxImpl.class); private final String tasksResourcePath; private final String serverUrl; private MBeanServerConnection connection; private volatile Boolean initialized = false; /** * Constructs a {@link VvrsResource} implementation using a {@link javax.management.MBeanServer} and * {@link JMX#newMXBeanProxy(MBeanServerConnection, ObjectName, Class) MXBeanProxies} as backend. * * @param serverUrl * the URL of the {@link javax.management.MBeanServer} */ public VvrsResourceJmxImpl(final String serverUrl) { this.serverUrl = serverUrl.trim(); this.tasksResourcePath = ResourcePath .extractPathForSubResourceLocator(this.getClass(), VvrsTasksResource.class); } /** * Gets the {@link UriBuilder} configured with this resource's absolute path. * * @param uriInfo * the request's {@link UriInfo} * @return a functional {@link UriBuilder} */ static final UriBuilder getVvrsUriBuilder(final UriInfo uriInfo) { // TODO: make this relative in case we're not at the root return uriInfo.getBaseUriBuilder().path(VvrsResource.class); } /** * This {@link VvrsResource}'s {@link CreateVvrResource} implementation. * * */ public final class CreateVvrResourceJmxImpl implements CreateVvrResource { @Override public Response createVvr(final String ownerId, final String uuid) { final VvrsTasksResourceJmxImpl taskResource = getVvrsTasksResource(); final VvrManagerMXBean vvrMgr = newVvrManagerProxy(InputValidation.getUuidFromString(ownerId)); // TODO: add name and description as query parameters final String taskId; try { if (Strings.isNullOrEmpty(uuid)) { taskId = vvrMgr.createVvrNoWait(null, null); } else { InputValidation.getUuidFromString(uuid); taskId = vvrMgr.createVvrNoWait(null, null, uuid); } final URI taskUri = taskResource.constructTaskUri(taskId); return Response.status(Status.ACCEPTED).location(taskUri).build(); } catch (final VvrManagementException e) { throw ServerErrorFactory.newInternalErrorException("Failed to create vvr", "Exception create vvr", e); } } } /** * Gets the currently active JMX connection. * * @return an open {@link MBeanServerConnection} * @throws CustomResourceException * if opening the connection fails */ final MBeanServerConnection getConnection() throws CustomResourceException { if (!initialized) { init(); } return connection; } /** * Creates a new proxy for the {@link VvrManagerMXBean} matching the provided owner UUID. * * @param ownerUuid * the owner's {@link UUID} * @return the vvrManagerProxy */ final VvrManagerMXBean newVvrManagerProxy(final UUID ownerUuid) { return JMX.newMXBeanProxy(getConnection(), VvrObjectNameFactory.newVvrManagerObjectName(ownerUuid), VvrManagerMXBean.class); } /** * Gets the set of all contained VVR instances. * * @param ownerId * the associated owner ID * @return a (possibly empty) set of {@link ObjectName}s representing the target VVRs * @throws CustomResourceException * if querying the remote JMX server fails */ final Set<ObjectName> getAllVvrInstances(final String ownerId) throws CustomResourceException { final UUID ownerUuid = InputValidation.getUuidFromString(ownerId); // TODO: include filters in query Set<ObjectName> vvrInstances; try { vvrInstances = getConnection().queryNames(VvrObjectNameFactory.newVvrQueryListObjectName(ownerUuid), Query.eq(Query.attr("OwnerUuid"), Query.value(ownerId))); } catch (final IOException e) { LOGGER.error("Exception querying VVRs", e); throw ServerErrorFactory.newInternalErrorException("Internal communication error", "Exception querying VVR instances", e); } return vvrInstances; } @Override public final VersionedVolumeRepositoryList getAllVvr(final String ownerId, final String ownerIdFilter, final String machineIdFilter, final String dcIdFilter) throws CustomResourceException { final MBeanServerConnection jmxConnection = getConnection(); final Set<ObjectName> vvrInstances = getAllVvrInstances(ownerId); final VersionedVolumeRepositoryList result = getObjectFactory().createVersionedVolumeRepositoryList(); final List<VersionedVolumeRepository> vvrList = result.getVersionedVolumeRepositories(); for (final ObjectName currObjName : vvrInstances) { vvrList.add(getVvrPojoFromMbeanProxy(JMX.newMXBeanProxy(jmxConnection, currObjName, VvrMXBean.class))); } return result; } @Override public final VvrResource getVvrResource(final String ownerId, final String vvrId) throws CustomResourceException { final UUID ownerUuid = InputValidation.getUuidFromString(ownerId); final UUID vvrUuid = InputValidation.getUuidFromString(vvrId); final MBeanServerConnection jmxConn = getConnection(); final ObjectName wantedVvrObjName = VvrObjectNameFactory.newVvrObjectName(ownerUuid, vvrUuid); try { final Set<ObjectName> foundBeans = jmxConn.queryNames(wantedVvrObjName, null); if (foundBeans.isEmpty()) { return null; } } catch (final IOException e) { LOGGER.error("Failed to query server for VVR", e); throw ServerErrorFactory.newInternalErrorException("Internal communication error", "Exception querying VVR instance", e); } VvrResourceJmxImpl result; final URI vvrResUri = uriInfo.getBaseUriBuilder().path(uriInfo.getMatchedURIs().get(0)).build(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Getting VVR resource for URI " + vvrResUri.toString()); } result = new VvrResourceJmxImpl(JMX.newMXBeanProxy(jmxConn, wantedVvrObjName, VvrMXBean.class), vvrResUri); ResourcePath.injectUriInfoContext(uriInfo, result); return result; } /** * Gets the POJO representation of the given JMX proxy. * * @param vvrProxy * a non-<code>null</code> {@link VvrMXBean} proxy * @return a newly created {@link VersionedVolumeRepository} representing the input argument */ static final VersionedVolumeRepository getVvrPojoFromMbeanProxy(final VvrMXBean vvrProxy) { final VersionedVolumeRepository result = getObjectFactory().createVersionedVolumeRepository(); // FIXME: quota, instanceCount are not present with VvrMXBean, so neither are persisted result.setDescription(vvrProxy.getDescription()); result.setInitialized(vvrProxy.isInitialized()); result.setInstanceCount(1); result.setName(vvrProxy.getName()); result.setOwnerid(vvrProxy.getOwnerUuid()); result.setQuota(Long.MAX_VALUE); result.setStarted(vvrProxy.isStarted()); result.setUuid(vvrProxy.getUuid()); return result; } private final void init() throws CustomResourceException { synchronized (initialized) { try { connection = JmxClientConnectionFactory.newConnection(serverUrl); } catch (NullPointerException | SecurityException | IOException e) { LOGGER.error("Exception getting connection", e); throw ServerErrorFactory.newInternalErrorException("Internal communication error", "Exception getting connection", e); } initialized = true; } } @Override public final CreateVvrResource getCreateVvrResource(final String ownerId) { return new CreateVvrResourceJmxImpl(); } @Override public final VvrsTasksResourceJmxImpl getVvrsTasksResource() { final URI tasksUri; final URI resourceUri; if (uriInfo == null) { LOGGER.warn("No injected URI information found, constructing tasks resource with default relative URI"); tasksUri = UriBuilder.fromPath(tasksResourcePath).build(); resourceUri = UriBuilder.fromPath("/").build(); // empty } else { tasksUri = getVvrsUriBuilder(uriInfo).path(tasksResourcePath).build(); resourceUri = getVvrsUriBuilder(uriInfo).build(); } final VvrsTasksResourceJmxImpl vvrsTasksRes = new VvrsTasksResourceJmxImpl(tasksUri, this, resourceUri); return vvrsTasksRes; } @Override protected final URI getResourceUri() { return getVvrsUriBuilder(uriInfo).build(); } }