Java tutorial
/************************************************************************* * (c) Copyright 2017 Hewlett Packard Enterprise Development Company LP * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. ************************************************************************/ package com.eucalyptus.cluster; import static com.eucalyptus.compute.common.internal.vm.VmInstance.VmStateSet.TORNDOWN; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.log4j.Logger; import org.hibernate.criterion.Restrictions; import com.eucalyptus.bootstrap.Databases; import com.eucalyptus.cluster.common.ClusterController; import com.eucalyptus.cluster.common.Cluster; import com.eucalyptus.cluster.common.vm.VmStateUpdate; import com.eucalyptus.cluster.common.msgs.AttachedVolume; import com.eucalyptus.cluster.common.msgs.NetworkConfigType; import com.eucalyptus.cluster.common.msgs.VmInfo; import com.eucalyptus.cluster.common.msgs.VmTypeInfo; import com.eucalyptus.component.annotation.ComponentNamed; import com.eucalyptus.compute.common.internal.vm.MigrationState; import com.eucalyptus.compute.common.internal.vm.VmBundleTask; import com.eucalyptus.compute.common.internal.vm.VmInstance; import com.eucalyptus.compute.common.internal.vm.VmRuntimeState; import com.eucalyptus.compute.common.internal.vm.VmVolumeAttachment; import com.eucalyptus.compute.common.internal.vmtypes.VmType; import com.eucalyptus.compute.common.internal.vpc.NetworkInterface; import com.eucalyptus.compute.common.internal.vpc.NetworkInterfaceAttachment; import com.eucalyptus.compute.common.network.InstanceResourceReportType; import com.eucalyptus.compute.common.network.Networking; import com.eucalyptus.compute.common.network.UpdateInstanceResourcesType; import com.eucalyptus.entities.Entities; import com.eucalyptus.entities.EntityCache; import com.eucalyptus.entities.TransactionException; import com.eucalyptus.entities.TransactionResource; import com.eucalyptus.event.ClockTick; import com.eucalyptus.event.EventListener; import com.eucalyptus.event.Listeners; import com.eucalyptus.network.NetworkInfoBroadcaster; import com.eucalyptus.records.Logs; import com.eucalyptus.system.Threads; import com.eucalyptus.util.CollectionUtils; import com.eucalyptus.util.Either; import com.eucalyptus.util.HasName; import com.eucalyptus.util.NonNullFunction; import com.eucalyptus.util.TypeMapper; import com.eucalyptus.util.TypeMappers; import com.eucalyptus.vm.Bundles; import com.eucalyptus.vm.VmInstances; import com.eucalyptus.vmtypes.VmTypes; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; /** * */ @ComponentNamed public class VmStateHandler { private static Logger LOG = Logger.getLogger(VmStateHandler.class); static { Cluster.registerVmStateUpdateConsumer(VmStateHandler::updateVmInfo); } private static final ConcurrentMap<String, Long> pendingUpdates = Maps.newConcurrentMap(); private static final Supplier<Iterable<VmStateView>> instanceViewSupplier = Suppliers.memoizeWithExpiration( new EntityCache<>(VmInstance.named(null), Restrictions.not(VmInstance.criterion(TORNDOWN.array())), Sets.newHashSet("transientVolumeState.attachments"), Sets.newHashSet("bootRecord.machineImage", "bootRecord.vmType", "networkGroups"), TypeMappers.lookup(VmInstance.class, VmStateView.class)), 10, TimeUnit.SECONDS); public static void updateVmInfo(final VmStateUpdate stateUpdate) { UpdateInstanceResourcesType update = new UpdateInstanceResourcesType(); update.setPartition(stateUpdate.getCluster().getPartition()); update.setResources(TypeMappers.transform(stateUpdate, InstanceResourceReportType.class)); final boolean requestBroadcast = Networking.getInstance().update(update); if (Databases.isVolatile()) { return; } final Cluster cluster = stateUpdate.getCluster(); final Set<String> initialInstances = stateUpdate.getRequestedVms(); final List<VmInfo> vms = stateUpdate.getVmInfos(); final Map<String, VmStateView> localState = ImmutableMap.copyOf(CollectionUtils.putAll( instanceViewSupplier.get(), Maps.<String, VmStateView>newHashMapWithExpectedSize(vms.size()), HasName.GET_NAME, Functions.<VmStateView>identity())); final Set<String> reportedInstances = Sets.newHashSetWithExpectedSize(vms.size()); for (VmInfo vmInfo : vms) { reportedInstances.add(vmInfo.getInstanceId()); vmInfo.setPlacement(cluster.getConfiguration().getName()); VmTypeInfo typeInfo = vmInfo.getInstanceType(); if (typeInfo.getName() == null || "".equals(typeInfo.getName())) { for (VmType t : VmTypes.list()) { if (t.getCpu().equals(typeInfo.getCores()) && t.getDisk().equals(typeInfo.getDisk()) && t.getMemory().equals(typeInfo.getMemory())) { typeInfo.setName(t.getName()); } } } } final Set<String> unreportedInstances = Sets .newHashSet(Sets.difference(initialInstances, reportedInstances)); if (Databases.isVolatile()) { return; } final Set<String> unknownInstances = Sets.newHashSet(Sets.difference(reportedInstances, initialInstances)); final List<Optional<Runnable>> taskList = Lists.newArrayList(); for (final VmInfo runVm : vms) { if (initialInstances.contains(runVm.getInstanceId())) { taskList.add(UpdateTaskFunction.REPORTED.apply(context(localState, runVm))); } else if (unknownInstances.contains(runVm.getInstanceId())) { taskList.add(UpdateTaskFunction.UNKNOWN.apply(context(localState, runVm))); } } for (final String vmId : unreportedInstances) { taskList.add(UpdateTaskFunction.UNREPORTED.apply(context(localState, vmId))); } final Optional<Runnable> broadcastRequestRunnable = requestBroadcast ? Optional.<Runnable>of(new Runnable() { @Override public void run() { NetworkInfoBroadcaster.requestNetworkInfoBroadcast(); } }) : Optional.<Runnable>absent(); for (final Runnable task : Iterables.concat(Optional.presentInstances(taskList), broadcastRequestRunnable.asSet())) { Threads.enqueue(ClusterController.class, VmStateHandler.class, (Runtime.getRuntime().availableProcessors() * 2) + 1, Executors.callable(task)); } } private static void handleUnreported(final VmStateContext vmStateContext) { try { final String vmId = vmStateContext.input.getLeft(); final long intitialReportTimeoutMillis = VmInstances.VM_INITIAL_REPORT_TIMEOUT * 1000; final VmStateView vmView = vmStateContext.getLocalState().get(vmId); if (vmView != null && vmView.getState() == VmInstance.VmState.PENDING && (System.currentTimeMillis() - vmView.getLastUpdated()) < intitialReportTimeoutMillis) { return; } final VmInstance vm = VmInstances.lookupAny(vmId); if (VmInstance.VmState.PENDING.apply(vm) && vm.lastUpdateMillis() < intitialReportTimeoutMillis) { //do nothing during first VM_INITIAL_REPORT_TIMEOUT millis of instance life return; } else if (vm.isBlockStorage() && (VmInstances.Timeout.UNREPORTED.apply(vm) || VmInstances.Timeout.PENDING.apply(vm))) { VmInstances.stopped(vm); } else if (VmInstance.VmState.STOPPING.apply(vm)) { VmInstances.stopped(vm); } else if (VmInstance.VmState.SHUTTING_DOWN.apply(vm)) { VmInstances.terminated(vm); } else if (VmInstances.Timeout.TERMINATED.apply(vm)) { VmInstances.buried(vm); } else if (VmInstances.Timeout.BURIED.apply(vm)) { VmInstances.delete(vm); } else if (!vm.isBlockStorage() && (VmInstances.Timeout.UNREPORTED.apply(vm) || VmInstances.Timeout.PENDING.apply(vm))) { VmInstances.terminated(vm); } else if (VmInstance.VmStateSet.RUN.apply(vm) && VmRuntimeState.InstanceStatus.Ok.apply(vm)) { VmInstances.unreachable(vm); } } catch (final Exception ex) { LOG.error(ex); Logs.extreme().error(ex, ex); } } private static void handleReportedState(final VmStateContext vmStateContext) { final VmInfo runVm = vmStateContext.getInput().getRight(); final VmInstance.VmState runVmState = VmInstance.VmState.Mapper.get(runVm.getStateName()); try { final VmStateView vmView = vmStateContext.getLocalState().get(runVm.getInstanceId()); MigrationState migrationState = MigrationState.defaultValueOf(runVm.getMigrationStateName()); boolean updateRequired = false; if (vmView != null) { if (vmView.inState(VmInstance.VmStateSet.DONE)) { if (vmView.getReason() == VmInstance.Reason.EXPIRED) { handleUnknown(vmStateContext); } else { LOG.trace("Ignore state update to terminated instance: " + runVm.getInstanceId()); } return; } else if (vmView.getState() == VmInstance.VmState.RUNNING && System.currentTimeMillis() > vmView.getExpires()) { updateRequired = true; } else if (VmInstance.VmState.SHUTTING_DOWN.equals(runVmState)) { updateRequired = true; } else if (!vmView.inState(VmInstance.VmStateSet.RUN) && VmInstance.VmStateSet.RUN.contains(runVmState) && (System.currentTimeMillis() - vmView.getLastUpdated()) > (VmInstances.VOLATILE_STATE_TIMEOUT_SEC * 1000l)) { updateRequired = true; } else if (vmView.inState(VmInstance.VmStateSet.RUN)) { updateRequired = vmView.isBundling() || vmView.isMigrating() || migrationState.isMigrating() || runVmState != vmView.getState() || !Objects.equals(vmView.getGuestState(), runVm.getGuestStateName()) || !Objects.equals(vmView.getServiceTag(), runVm.getServiceTag()) || (System.currentTimeMillis() - vmView.getLastUpdated()) > VmInstances.Timeout.UNTOUCHED.getMilliseconds() || // for running and pending states vmView.getReachabilityStatus() != VmRuntimeState.ReachabilityStatus.Passed || (vmView.getState() == VmInstance.VmState.RUNNING && !vmView.getVolumeAttachments() .equals(runVm.getVolumes().stream() .map(TypeMappers.lookupF(AttachedVolume.class, VmStateVolumeAttachmentView.class)) .collect(Collectors.toMap(HasName.GET_NAME, volumeView -> volumeView)))) || (vmView.getState() == VmInstance.VmState.RUNNING && !vmView .getNetworkInterfaceAttachments() .equals(runVm.getSecondaryNetConfigList().stream() .map(TypeMappers.lookupF(NetworkConfigType.class, VmStateNetworkInterfaceAttachmentView.class)) .collect(Collectors.toMap(HasName.GET_NAME, niView -> niView)))); } } if (updateRequired) try (final TransactionResource db = Entities.transactionFor(VmInstance.class)) { VmInstance vm = VmInstances.lookupAny(runVm.getInstanceId()); if (VmInstances.Timeout.EXPIRED.apply(vm)) { if (vm.isBlockStorage()) { VmInstances.stopped(vm); } else { VmInstances.shutDown(vm); } } else if (VmInstance.VmState.SHUTTING_DOWN.equals(runVmState)) { db.rollback(); handleReportedTeardown(vm, runVm); return; } else { VmInstances.doUpdate(vm).apply(runVm); } Entities.commit(db); } catch (Exception ex) { LOG.error(ex); Logs.extreme().error(ex, ex); throw ex; } } catch (com.eucalyptus.compute.common.internal.vm.VmInstances.TerminatedInstanceException ex1) { LOG.trace("Ignore state update to terminated instance: " + runVm.getInstanceId()); } catch (NoSuchElementException ex1) { // VmStateCallback.handleRestore( runVm ); } catch (Exception ex1) { LOG.error(ex1); Logs.extreme().error(ex1, ex1); } } enum UpdateTaskFunction implements NonNullFunction<VmStateContext, Optional<Runnable>> { REPORTED { void task(final VmStateContext context) { handleReportedState(context); } }, UNKNOWN { @Override void task(final VmStateContext context) { handleUnknown(context); } }, UNREPORTED { @Override void task(final VmStateContext context) { handleUnreported(context); } }; abstract void task(final VmStateContext context); @Nonnull @Override public Optional<Runnable> apply(final VmStateContext context) { final String instanceId = context == null ? null : context.input.isLeft() ? context.input.getLeft() : context.input.getRight().getInstanceId(); try { final Runnable run = new Runnable() { @Override public void run() { try { UpdateTaskFunction.this.task(context); } catch (Exception e) { LOG.error("Failed to handle " + UpdateTaskFunction.this.name().toLowerCase() + " instance: " + instanceId + " because of " + e.getMessage()); } finally { pendingUpdates.remove(instanceId); } } }; if (context != null && instanceId != null && pendingUpdates.putIfAbsent(instanceId, System.currentTimeMillis()) == null) { return Optional.of(run); } else { return Optional.absent(); } } catch (Exception e) { return Optional.absent(); } } } private static void handleUnknown(final VmStateContext vmStateContext) { for (final Optional<VmInstances.RestoreHandler> restoreHandler : VmInstances.RestoreHandler .parseList(VmInstances.UNKNOWN_INSTANCE_HANDLERS)) { if (restoreHandler.isPresent() && handleRestore(vmStateContext.getInput().getRight(), restoreHandler.get())) { break; } } } private static boolean handleRestore(final VmInfo runVm, final Predicate<VmInfo> restorer) { final VmInstance.VmState runVmState = VmInstance.VmState.Mapper.get(runVm.getStateName()); if (VmInstance.VmStateSet.RUN.contains(runVmState)) { try { final VmInstance vm = VmInstances.lookupAny(runVm.getInstanceId()); if (!(VmInstance.VmStateSet.DONE.apply(vm) && VmInstance.Reason.EXPIRED.apply(vm))) { if (VmInstance.VmStateSet.TORNDOWN.apply(vm)) { VmInstances.RestoreHandler.Terminate.apply(runVm); } return true; } } catch (NoSuchElementException ex) { LOG.debug("Instance record not found for restore: " + runVm.getInstanceId()); Logs.extreme().error(ex, ex); } catch (Exception ex) { LOG.error(ex); Logs.extreme().error(ex, ex); } try { LOG.debug("Instance " + runVm.getInstanceId() + " " + runVm); return restorer.apply(runVm); } catch (Throwable ex) { LOG.error(ex); Logs.extreme().error(ex, ex); } } return false; } private static void handleReportedTeardown(VmInstance vm, final VmInfo runVm) throws TransactionException { /** * TODO:GRZE: based on current local instance state we need to handle reported * SHUTTING_DOWN state differently **/ VmBundleTask.BundleState bundleState = VmBundleTask.BundleState.mapper .apply(runVm.getBundleTaskStateName()); if (!VmBundleTask.BundleState.none.equals(bundleState)) { Bundles.updateBundleTaskState(vm, bundleState, 0.0d); VmInstances.terminated(vm); } else if (VmInstance.VmState.SHUTTING_DOWN.apply(vm)) { VmInstances.terminated(vm); } else if (VmInstance.VmState.STOPPING.apply(vm)) { VmInstances.stopped(vm); } else if (VmInstance.VmStateSet.RUN.apply(vm) && vm.getSplitTime() > (VmInstances.VM_STATE_SETTLE_TIME * 1000)) { if (vm.isBlockStorage()) { VmInstances.stopped(vm); } else { VmInstances.shutDown(vm); } } } private static VmStateContext context(final Map<String, VmStateView> localState, final String vmId) { return new VmStateContext(localState, vmId); } private static VmStateContext context(final Map<String, VmStateView> localState, final VmInfo vmInfo) { return new VmStateContext(localState, vmInfo); } @TypeMapper public enum VmStateUpdateToInstanceResourceReport implements Function<VmStateUpdate, InstanceResourceReportType> { INSTANCE; @Nullable @Override public InstanceResourceReportType apply(final VmStateUpdate vmStateUpdate) { final InstanceResourceReportType report = new InstanceResourceReportType(); for (final VmInfo vmInfo : vmStateUpdate.getVmInfos()) { if (!"Teardown".equals(vmInfo.getStateName()) && vmInfo.getNetParams() != null) { report.getPublicIps().add(vmInfo.getNetParams().getIgnoredPublicIp()); report.getPrivateIps().add(vmInfo.getNetParams().getIpAddress()); report.getMacs().add(vmInfo.getNetParams().getMacAddress()); if (vmInfo.getSecondaryNetConfigList() != null && vmInfo.getSecondaryNetConfigList().size() > 0) { for (NetworkConfigType netConfig : vmInfo.getSecondaryNetConfigList()) { report.getPublicIps().add(netConfig.getIgnoredPublicIp()); report.getPrivateIps().add(netConfig.getIpAddress()); report.getMacs().add(netConfig.getMacAddress()); } } } } return report; } } public static final class VmStateVolumeAttachmentView implements HasName<VmStateVolumeAttachmentView> { private final String id; private final String device; private final String removeDevice; private final String status; private final Long attachTime; public VmStateVolumeAttachmentView(final String id, final String device, final String removeDevice, final String status, final Long attachTime) { this.id = id; this.device = device; this.removeDevice = removeDevice; this.status = status; this.attachTime = attachTime; } public String getId() { return id; } @Override public String getName() { return id; } @Override public int compareTo(final VmStateVolumeAttachmentView o) { return id.compareTo(o.id); } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final VmStateVolumeAttachmentView that = (VmStateVolumeAttachmentView) o; return Objects.equals(id, that.id) && Objects.equals(device, that.device) && Objects.equals(removeDevice, that.removeDevice) && Objects.equals(status, that.status); } @Override public int hashCode() { return Objects.hash(id, device, removeDevice, status); } } public static final class VmStateNetworkInterfaceAttachmentView implements HasName<VmStateNetworkInterfaceAttachmentView> { private final String networkInterfaceId; private final String attachmentId; public VmStateNetworkInterfaceAttachmentView(final String networkInterfaceId, final String attachmentId) { this.networkInterfaceId = networkInterfaceId; this.attachmentId = attachmentId; } public String getId() { return networkInterfaceId; } @Override public String getName() { return networkInterfaceId; } @Override public int compareTo(final VmStateNetworkInterfaceAttachmentView o) { return attachmentId.compareTo(o.attachmentId); } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final VmStateNetworkInterfaceAttachmentView that = (VmStateNetworkInterfaceAttachmentView) o; return Objects.equals(attachmentId, that.attachmentId); } @Override public int hashCode() { return attachmentId.hashCode(); } } public static final class VmStateView implements HasName<VmStateView> { private final String id; private final int version; private final String partition; private final String serviceTag; private final VmInstance.VmState state; private final String guestState; private final VmRuntimeState.ReachabilityStatus reachabilityStatus; private final VmInstance.Reason reason; private final Map<String, VmStateVolumeAttachmentView> volumeAttachments; private final Map<String, VmStateNetworkInterfaceAttachmentView> networkInterfaceAttachments; private final long lastUpdated; private final long expires; private final boolean bundling; private final boolean migrating; public VmStateView(final String id, final int version, final String partition, final String serviceTag, final VmInstance.VmState state, final String guestState, final VmRuntimeState.ReachabilityStatus reachabilityStatus, final VmInstance.Reason reason, final Map<String, VmStateVolumeAttachmentView> volumeAttachments, final Map<String, VmStateNetworkInterfaceAttachmentView> networkInterfaceAttachments, final long lastUpdated, final long expires, final boolean bundling, final boolean migrating) { this.id = id; this.version = version; this.partition = partition; this.serviceTag = serviceTag; this.state = state; this.guestState = guestState; this.reachabilityStatus = reachabilityStatus; this.reason = reason; this.volumeAttachments = volumeAttachments; this.networkInterfaceAttachments = networkInterfaceAttachments; this.lastUpdated = lastUpdated; this.expires = expires; this.bundling = bundling; this.migrating = migrating; } public String getId() { return id; } public int getVersion() { return version; } public String getPartition() { return partition; } public String getServiceTag() { return serviceTag; } public VmInstance.VmState getState() { return state; } public String getGuestState() { return guestState; } public VmRuntimeState.ReachabilityStatus getReachabilityStatus() { return reachabilityStatus; } public VmInstance.Reason getReason() { return reason; } public Map<String, VmStateVolumeAttachmentView> getVolumeAttachments() { return volumeAttachments; } public Map<String, VmStateNetworkInterfaceAttachmentView> getNetworkInterfaceAttachments() { return networkInterfaceAttachments; } public long getLastUpdated() { return lastUpdated; } public long getExpires() { return expires; } public boolean isBundling() { return bundling; } public boolean isMigrating() { return migrating; } @Override public String getName() { return id; } @Override public int compareTo(final VmStateView o) { return id.compareTo(o.id); } public boolean inState(final VmInstance.VmStateSet stateSet) { return stateSet.set().contains(state); } } @TypeMapper public enum VmInstanceToVmStateView implements Function<VmInstance, VmStateView> { INSTANCE; final Set<NetworkInterfaceAttachment.Status> attachedStates = EnumSet .of(NetworkInterfaceAttachment.Status.attached, NetworkInterfaceAttachment.Status.detaching); @Override public VmStateView apply(final VmInstance vmInstance) { final Map<String, VmStateVolumeAttachmentView> volumes = Stream .concat(vmInstance.getBootRecord().getPersistentVolumes().stream(), vmInstance.getTransientVolumeState().getAttachments().stream()) .map(TypeMappers.lookupF(VmVolumeAttachment.class, VmStateVolumeAttachmentView.class)) .collect(Collectors.toMap(HasName.GET_NAME, volumeView -> volumeView)); final Map<String, VmStateNetworkInterfaceAttachmentView> networkInterfaces = vmInstance .getNetworkInterfaces().stream() .filter(ni -> ni.getAttachment().getDeviceIndex() != 0 && attachedStates.contains(ni.getAttachment().getStatus())) .map(TypeMappers.lookupF(NetworkInterface.class, VmStateNetworkInterfaceAttachmentView.class)) .collect(Collectors.toMap(HasName.GET_NAME, niView -> niView)); return new VmStateView(vmInstance.getInstanceId(), vmInstance.getVersion(), vmInstance.getPartition(), vmInstance.getServiceTag(), vmInstance.getState(), vmInstance.getRuntimeState().getGuestState(), vmInstance.getRuntimeState().getReachabilityStatus(), vmInstance.getRuntimeState().getReason(), ImmutableMap.copyOf(volumes), ImmutableMap.copyOf(networkInterfaces), vmInstance.getLastUpdateTimestamp().getTime(), vmInstance.getExpiration() == null ? Long.MAX_VALUE : vmInstance.getExpiration().getTime(), vmInstance.getRuntimeState().isBundling(), vmInstance.getRuntimeState().getMigrationTask().getState().isMigrating()); } } private static final class VmStateContext { private final Map<String, VmStateView> localState; private final Either<String, VmInfo> input; VmStateContext(final Map<String, VmStateView> localState, final String vmId) { this.localState = localState; this.input = Either.left(vmId); } VmStateContext(final Map<String, VmStateView> localState, final VmInfo vmInfo) { this.localState = localState; this.input = Either.right(vmInfo); } public Map<String, VmStateView> getLocalState() { return localState; } public Either<String, VmInfo> getInput() { return input; } } @TypeMapper public enum AttachedVolumeToVmStateVolumeAttachmentView implements Function<AttachedVolume, VmStateVolumeAttachmentView> { INSTANCE; @Override public VmStateVolumeAttachmentView apply(final AttachedVolume attachedVolume) { return new VmStateVolumeAttachmentView(attachedVolume.getVolumeId(), attachedVolume.getDevice(), attachedVolume.getRemoteDevice(), attachedVolume.getStatus(), attachedVolume.getAttachTime() == null ? null : attachedVolume.getAttachTime().getTime()); } } @TypeMapper public enum VmVolumeAttachmentToVmStateVolumeAttachmentView implements Function<VmVolumeAttachment, VmStateVolumeAttachmentView> { INSTANCE; @Override public VmStateVolumeAttachmentView apply(final VmVolumeAttachment volumeAttachment) { return new VmStateVolumeAttachmentView(volumeAttachment.getVolumeId(), volumeAttachment.getDevice(), volumeAttachment.getRemoteDevice(), volumeAttachment.getStatus(), volumeAttachment.getAttachTime() == null ? null : volumeAttachment.getAttachTime().getTime()); } } @TypeMapper public enum NetworkConfigTypeToVmStateNetworkInterfaceAttachmentView implements Function<NetworkConfigType, VmStateNetworkInterfaceAttachmentView> { INSTANCE; @Override public VmStateNetworkInterfaceAttachmentView apply(final NetworkConfigType networkConfigType) { return new VmStateNetworkInterfaceAttachmentView(networkConfigType.getInterfaceId(), networkConfigType.getAttachmentId()); } } @TypeMapper public enum NetworkInterfaceToVmStateNetworkInterfaceAttachmentView implements Function<NetworkInterface, VmStateNetworkInterfaceAttachmentView> { INSTANCE; @Override public VmStateNetworkInterfaceAttachmentView apply(final NetworkInterface networkInterface) { return new VmStateNetworkInterfaceAttachmentView(networkInterface.getDisplayName(), networkInterface.getAttachment().getAttachmentId()); } } private static final class StateTaskExpiryEventListener implements EventListener<ClockTick> { public static void register() { Listeners.register(ClockTick.class, new StateTaskExpiryEventListener()); } @Override public void fireEvent(final ClockTick event) { final long expiry = System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5); for (final Map.Entry<String, Long> entry : pendingUpdates.entrySet()) { if (entry.getValue() < expiry) { if (pendingUpdates.remove(entry.getKey(), entry.getValue())) { LOG.warn("Expired state update task for instance " + entry.getKey()); } } } } } }