package io.fathom.cloud.blobs.replicated; import io.fathom.cloud.CloudException; import io.fathom.cloud.blobs.sftp.SftpBlobStore; import io.fathom.cloud.cluster.ClusterService; import io.fathom.cloud.mq.MessageQueueService; import io.fathom.cloud.mq.MessageQueueWriter; import io.fathom.cloud.mq.QueuedRequestExecutor; import io.fathom.cloud.protobuf.CloudCommons.NodeType; import io.fathom.cloud.sftp.RemoteFile; import io.fathom.cloud.ssh.SshConfig; import io.fathom.cloud.ssh.SshContext; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.List; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fathomdb.Configuration; import com.google.common.collect.Lists; import com.google.common.net.InetAddresses; import com.google.inject.persist.Transactional; @Transactional public class StorageClusterBuilder { private static final Logger log = LoggerFactory.getLogger(StorageClusterBuilder.class); @Inject ClusterService clusterService; @Inject SshContext sshContext; @Inject Configuration config; @Inject MessageQueueService messageQueues; public StorageCluster build(int dataReplicaCount, String ifModifiedSince) throws IOException, CloudException { File localCacheDir = config.lookupFile("objectstore.cachedir", "/var/openstack/objectstore/cache"); // Note that we get the etag first... // Any concurrent changes may be in the new version // i.e. we may end up doing a spurious rebuild String etag = clusterService.getEtag(); if (ifModifiedSince != null && ifModifiedSince.equals(etag)) { return null; } List<StorageNode> nodes = Lists.newArrayList(); for (ClusterService.Node nodeData : clusterService.findNodes(NodeType.STORAGE)) { String key = nodeData.getKey(); // TODO: Don't use sftp for self-connections!! InetAddress bestAddress = null; for (String s : nodeData.getAddressList()) { InetAddress address = InetAddresses.forString(s); if (bestAddress == null) { bestAddress = address; } else { log.warn("Comparison between node addresses not implemented: {} vs {}", bestAddress, address); } } if (bestAddress == null) { throw new IllegalStateException("Unable to find suitable address to communicate with node: " + nodeData); } InetSocketAddress sshSocketAddress = new InetSocketAddress(bestAddress, 22); SshConfig sshConfig = sshContext.buildConfig(sshSocketAddress); SftpBlobStore.Factory blobStoreFactory; String store = nodeData.getStore(); if (store.startsWith("sftp://")) { File path = new File(store.substring(7)); blobStoreFactory = new SftpBlobStore.Factory(sshConfig, new RemoteFile(path), localCacheDir); } else { throw new IllegalArgumentException(); } String queue = nodeData.getQueue(); MessageQueueWriter mqWriter = messageQueues.getWriter(sshConfig, queue); nodes.add(new StorageNode(key, blobStoreFactory, new QueuedRequestExecutor(mqWriter))); } return new StorageCluster(etag, dataReplicaCount, nodes); } public StorageCluster build() throws IOException, CloudException { int dataReplicaCount = getConfiguredDataReplicaCount(); return build(dataReplicaCount, null); } public int getConfiguredDataReplicaCount() { return config.lookup("objectstore.replicas", 1); } }