package io.eguan.vvr.persistence.repository; /* * #%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.nrs.NrsFile; import io.eguan.nrs.NrsFileFlag; import io.eguan.nrs.NrsFileHeader; import io.eguan.proto.Common.OpCode; import io.eguan.proto.Common.Type; import io.eguan.proto.vvr.VvrRemote; import io.eguan.utils.SimpleIdentifierProvider; import io.eguan.utils.UuidT; import io.eguan.vvr.remote.VvrRemoteUtils; import io.eguan.vvr.repository.core.api.FutureDevice; import io.eguan.vvr.repository.core.api.FutureVoid; import io.eguan.vvr.repository.core.api.Snapshot; import java.io.IOException; import java.util.Collection; import java.util.Date; import java.util.EnumSet; import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.UUID; import javax.annotation.Nonnull; import javax.naming.OperationNotSupportedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Strings; /** * Snapshot implementation. * <p> * This class adds the logic specific to snapshots to the more generic {@link AbstractSimpleStorageVolume}. * * @author oodrive * @author llambert * @author ebredzinski * @author jmcaba * */ public final class NrsSnapshot extends NrsVvrItem implements Snapshot { private static final Logger LOGGER = LoggerFactory.getLogger(NrsSnapshot.class); static final String DELETED_KEY = "deleted"; static final String NRSFILE_UUID_KEY = "fileuuid"; /** * Flag for indicating snapshots that are considered deleted. */ private boolean deleted = false; /** * Flag for the root snapshot. */ private final boolean root; /** * Builder constructor called by {@link NrsSnapshot.Builder}s. * * @param builder * the configured builder */ private NrsSnapshot(final NrsSnapshot.BuilderLoad builder) { super(builder); LOGGER.trace("Load new {} instance with uuid {}", NrsSnapshot.class.getSimpleName(), this.getUuid()); this.deleted = builder.deleted(); this.root = getNrsFilePath().getDescriptor().isRoot(); } /** * Builder constructor called by {@link NrsSnapshot.Builder}s. * * @param builder * the configured builder */ private NrsSnapshot(final NrsSnapshot.BuilderCreate builder) { super(builder); LOGGER.trace("Create new {} instance with uuid {}", NrsSnapshot.class.getSimpleName(), this.getUuid()); this.deleted = false; this.root = getNrsFilePath().getDescriptor().isRoot(); } /** * Builder for the root snapshot of a {@link NrsRepository}. * * @param builder */ private NrsSnapshot(final NrsSnapshot.BuilderRoot builder) { super(builder); LOGGER.trace("Create root with uuid {}", this.getUuid()); this.root = getNrsFilePath().getDescriptor().isRoot(); assert root == true; } /* * (non-Javadoc) * * @see io.eguan.vvr.persistence.repository.NrsVvrItem#getPersistenceProperties() */ @Override protected final Properties getPersistenceProperties() { final Properties properties = super.getPersistenceProperties(); properties.setProperty(DELETED_KEY, Boolean.valueOf(deleted).toString()); properties.setProperty(NRSFILE_UUID_KEY, getNrsFileId().toString()); return properties; } @Override public final FutureDevice createDevice(final String name) { return createDevice(name, UUID.randomUUID()); } @Override public final FutureDevice createDevice(final String name, final UUID uuid) { return createDevice(name, null, uuid); } @Override public final FutureDevice createDevice(final String name, final long size) { return createDevice(name, null, UUID.randomUUID(), size); } @Override public final FutureDevice createDevice(final String name, final UUID uuid, final long size) { return createDevice(name, null, uuid, size); } @Override public final FutureDevice createDevice(final String name, final String description) { return createDevice(name, description, getSize()); } @Override public final FutureDevice createDevice(final String name, final String description, final UUID uuid) { return createDevice(name, description, uuid, getSize()); } @Override public final FutureDevice createDevice(final String name, final String description, final long size) { return createDevice(name, description, UUID.randomUUID(), size); } @Override public final FutureDevice createDevice(final String name, final String description, final UUID uuid, final long size) { if (Strings.isNullOrEmpty(name)) { throw new IllegalArgumentException("name is null"); } // Create payload final NrsRepository repository = getVvr(); final UUID deviceUuid = Objects.requireNonNull(uuid); final UuidT<NrsFile> futureSnapshotUuid = SimpleIdentifierProvider.newId(); final NrsFileHeader<NrsFile> deviceNrsFileHeader = repository.doCreateFutureNrsFileHeader(getNrsFileId(), size, deviceUuid, futureSnapshotUuid); // Create and launch transaction final VvrRemote.RemoteOperation.Builder opBuilder = VvrRemote.RemoteOperation.newBuilder(); NrsRemoteUtils.addNrsDevice(opBuilder, deviceNrsFileHeader, getUuid(), name, description); final UUID taskId = repository.submitTransaction(opBuilder, Type.DEVICE, OpCode.CREATE); // Get the device future return new NrsFutureDevice(deviceUuid, repository, taskId); } @Override public final byte[] export() throws OperationNotSupportedException { throw new OperationNotSupportedException("not yet implemented"); } @Override public final FutureVoid delete() { final UUID snapshotUuid = getUuid(); final VvrRemote.RemoteOperation.Builder opBuilder = VvrRemote.RemoteOperation.newBuilder(); opBuilder.setUuid(VvrRemoteUtils.newUuid(snapshotUuid)); final NrsRepository repository = getVvr(); final UUID taskId = repository.submitTransaction(opBuilder, Type.SNAPSHOT, OpCode.DELETE); return new NrsFutureVoid(repository, taskId, snapshotUuid); } final void deleteSnapshot() { final NrsRepository repository = getVvr(); repository.unregisterSnapshot(this.getUuid()); this.deleted = true; try { this.persist(); } catch (final IOException e) { throw new IllegalStateException(e); } } @Override public final Date getSnapshotTime() { return new Date(getTimestamp()); } @Override public final Collection<UUID> getChildrenSnapshotsUuid() { return getVvr().getSnapshotChildrenUuid(this.getUuid()); } @Override public final Collection<UUID> getSnapshotDevicesUuid() { return getVvr().getSnapshotDevicesUuid(this.getUuid()); } /** * Gets the deleted state of this snapshot. * * @return the value of the deleted flag */ protected final boolean isDeleted() { return this.deleted; } /** * Tells if the {@link Snapshot} is the root snapshot. * * @return <code>true</code> for the root snapshot. */ final boolean isRoot() { return root; } /** * Load a snapshot from the persistence. */ public static final class BuilderLoad extends NrsVvrItem.Builder { private NrsFileHeader<NrsFile> header; public final Snapshot.Builder header(final @Nonnull NrsFileHeader<NrsFile> header) { this.header = Objects.requireNonNull(header); return this; } private boolean deleted; public final Snapshot.Builder deleted(final boolean deleted) { this.deleted = deleted; return this; } public final boolean deleted() { return this.deleted; } @Override protected final UUID deviceID() { return header.getDeviceId(); } @Override protected final UUID nodeID() { return header.getNodeId(); } @Override protected final UuidT<NrsFile> futureID() { return header.getFileId(); } public final NrsSnapshot build() { size(header.getSize()); flags(header.getFlags()); return new NrsSnapshot(this); } } /** * Take a new snapshot of a device. */ public static final class BuilderCreate extends NrsVvrItem.Builder { private NrsDevice sourceDevice; /** * Source device of which the snapshot will be taken. * <p> * Mandatory. */ public final Snapshot.Builder device(final @Nonnull NrsDevice device) { this.sourceDevice = Objects.requireNonNull(device); // Inherit size and flags from the device size(device.getSize()); flags(device.getNrsFilePath().getDescriptor().getFlags()); return this; } private NrsFileHeader<NrsFile> deviceFutureNrsFileHeader; /** * Sets the header of the future {@link NrsFile} of the device. * * @param nrsFileHeader * @return this */ protected final NrsVvrItem.Builder deviceFutureNrsFileHeader(final @Nonnull NrsFileHeader<NrsFile> nrsFileHeader) { this.deviceFutureNrsFileHeader = Objects.requireNonNull(nrsFileHeader); return this; } @Override protected final UUID deviceID() { return sourceDevice.getUuid(); } @Override protected final UUID nodeID() { return sourceDevice.getNodeId(); } @Override protected final UuidT<NrsFile> futureID() { return sourceDevice.getNrsFileId(); } /** * Create a new snapshot. * * @return the created snapshot. */ public final NrsSnapshot create() { // Check UUID conflict final NrsRepository repository = getVvr(); { final UUID uuid = uuid(); if (repository.getSnapshot(uuid) != null) { throw new IllegalStateException("Failed to create snapshot, duplicate uuid=" + uuid); } if (repository.getDevice(uuid) != null) { throw new IllegalStateException("Failed to create snapshot, duplicate uuid=" + uuid + " (device)"); } } // Set NrsFile sourceFile(sourceDevice.getNrsFilePath()); // Creates the snapshot final NrsSnapshot result = new NrsSnapshot(this); try { result.create(); } catch (final IOException e) { throw new IllegalStateException("Failed to create snapshot", e); } repository.preRegisterSnapshot(result); // New NrsFile for the source device: create from NRS template sourceDevice.createNewNrsFile(deviceFutureNrsFileHeader); repository.postRegisterSnapshot(result); return result; } } /** * Member builder for create the root snapshot of a new repository. * * */ public static final class BuilderRoot extends NrsVvrItem.Builder { @Override protected final UUID deviceID() { // Dummy device ID: set ownerID return getVvr().getOwnerId(); } @Override protected final UUID nodeID() { // Current node for the root return getVvr().getNodeId(); } @Override protected final UuidT<NrsFile> futureID() { // Same uuid has the root snapshot return SimpleIdentifierProvider.fromUUID(uuid()); } /** * Create the root snapshot. * * @return the created snapshot. */ public final NrsSnapshot create() { name(""); // Root has no name (yet) description(""); // Root has no description (yet) // Set id=parentId and force size to 0 parentFile(futureID()); size(0); final Set<NrsFileFlag> flags = EnumSet.noneOf(NrsFileFlag.class); flags.add(NrsFileFlag.ROOT); flags(flags); // Create and set NrsFile final NrsFile nrsFile = createNrsFile(null); sourceFile(nrsFile); // Can not write the file of the root snapshot getVvr().getNrsFileJanitor().setNrsFileNoWritable(nrsFile); // Set owner ID as device ID (in method deviceID()) final NrsSnapshot result = new NrsSnapshot(this); try { result.create(); } catch (final IOException e) { // TODO remove NrsFile throw new IllegalStateException("Failed to create root snapshot", e); } return result; } } }