Repository: incubator-brooklyn Updated Branches: refs/heads/master 3559b5d6d -> df4eb8a7f
BROOKLYN-143 - support for Hazelcast Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/e37f0585 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/e37f0585 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/e37f0585 Branch: refs/heads/master Commit: e37f05856c7067081f8e37d3fbdc84408e1bbb9d Parents: b2d3f33 Author: Yavor Yanchev <[email protected]> Authored: Wed May 13 03:05:54 2015 +0300 Committer: Aled Sage <[email protected]> Committed: Thu Jul 23 23:37:23 2015 -0700 ---------------------------------------------------------------------- sandbox/nosql/pom.xml | 14 ++ .../nosql/hazelcast/HazelcastCluster.java | 59 +++++++ .../nosql/hazelcast/HazelcastClusterImpl.java | 124 +++++++++++++++ .../entity/nosql/hazelcast/HazelcastNode.java | 97 +++++++++++ .../nosql/hazelcast/HazelcastNodeDriver.java | 25 +++ .../nosql/hazelcast/HazelcastNodeImpl.java | 148 +++++++++++++++++ .../nosql/hazelcast/HazelcastNodeSshDriver.java | 159 +++++++++++++++++++ .../nosql/hazelcast/hazelcast-brooklyn.xml | 65 ++++++++ .../hazelcast/HazelcastClusterEc2LiveTest.java | 71 +++++++++ .../HazelcastClusterSoftlayerLiveTest.java | 71 +++++++++ 10 files changed, 833 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/pom.xml ---------------------------------------------------------------------- diff --git a/sandbox/nosql/pom.xml b/sandbox/nosql/pom.xml index 2de79ac..da6fe21 100644 --- a/sandbox/nosql/pom.xml +++ b/sandbox/nosql/pom.xml @@ -91,6 +91,20 @@ </execution> </executions> </plugin> + <plugin> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludes combine.children="append"> + <!-- + Configuration artifacts (for installations) are based on templated defaults for + the given components. These are files "without any degree of creativity" from the + perspective of the Brooklyn/Apache contribution. + --> + <exclude>src/main/resources/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml</exclude> + </excludes> + </configuration> + </plugin> </plugins> </build> </project> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java ---------------------------------------------------------------------- diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java new file mode 100644 index 0000000..48ab1aa --- /dev/null +++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastCluster.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package brooklyn.entity.nosql.hazelcast; + +import java.util.List; + +import com.google.common.reflect.TypeToken; + +import brooklyn.catalog.Catalog; +import brooklyn.config.ConfigKey; +import brooklyn.entity.basic.ConfigKeys; +import brooklyn.entity.group.DynamicCluster; +import brooklyn.entity.proxying.ImplementedBy; +import brooklyn.event.AttributeSensor; +import brooklyn.event.basic.BasicAttributeSensorAndConfigKey; +import brooklyn.event.basic.Sensors; +import brooklyn.util.flags.SetFromFlag; + +/** + * A cluster of {@link HazelcastNode}s based on {@link DynamicCluster}. + */ +@Catalog(name="Hazelcast Cluster", description="Hazelcast is a clustering and highly scalable data distribution platform for Java.") + +@ImplementedBy(HazelcastClusterImpl.class) +public interface HazelcastCluster extends DynamicCluster { + + @SetFromFlag("clusterName") + BasicAttributeSensorAndConfigKey<String> CLUSTER_NAME = new BasicAttributeSensorAndConfigKey<String>(String.class, + "hazelcast.cluster.name", "Name of the Hazelcast cluster", "HazelcastCluster"); + + @SetFromFlag("clusterPassword") + ConfigKey<String> CLUSTER_PASSWORD = + ConfigKeys.newStringConfigKey("hazelcast.cluster.password", "Hazelcast cluster password."); + + @SuppressWarnings("serial") + AttributeSensor<List<String>> PUBLIC_CLUSTER_NODES = Sensors.newSensor(new TypeToken<List<String>>() {}, + "hazelcast.cluster.public.nodes", "List of public addresses of all nodes in the cluster"); + + String getClusterName(); + + String getClusterPassword(); + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java ---------------------------------------------------------------------- diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java new file mode 100644 index 0000000..e911318 --- /dev/null +++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterImpl.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package brooklyn.entity.nosql.hazelcast; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Lists; +import brooklyn.entity.Entity; +import brooklyn.entity.basic.Attributes; +import brooklyn.entity.basic.EntityInternal; +import brooklyn.entity.group.AbstractMembershipTrackingPolicy; +import brooklyn.entity.group.DynamicClusterImpl; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.location.Location; +import brooklyn.policy.PolicySpec; +import brooklyn.util.text.Strings; + +public class HazelcastClusterImpl extends DynamicClusterImpl implements HazelcastCluster { + private static final Logger LOG = LoggerFactory.getLogger(HazelcastClusterImpl.class); + + private static final AtomicInteger nextMemberId = new AtomicInteger(0); + + @Override + protected EntitySpec<?> getMemberSpec() { + EntitySpec<?> spec = EntitySpec.create(getConfig(MEMBER_SPEC, EntitySpec.create(HazelcastNode.class))); + + spec.configure(HazelcastNode.GROUP_NAME, getConfig(HazelcastClusterImpl.CLUSTER_NAME)); + + if (LOG.isInfoEnabled()) { + LOG.info("Cluster name : {} : used as a group name", getConfig(HazelcastNode.GROUP_NAME)); + } + + spec.configure(HazelcastNode.GROUP_PASSWORD, getClusterPassword()); + + return spec; + } + + @Override + public void init() { + super.init(); + + String clusterPassword = getClusterPassword(); + + if (Strings.isBlank(clusterPassword)) { + if (LOG.isInfoEnabled()) { + LOG.info(this + " cluster password not provided for " + CLUSTER_PASSWORD.getName() + " : generating random password"); + } + setConfig(CLUSTER_PASSWORD, Strings.makeRandomId(12)); + } + + addPolicy(PolicySpec.create(MemberTrackingPolicy.class) + .displayName("Hazelcast members tracker") + .configure("group", this)); + } + + public static class MemberTrackingPolicy extends AbstractMembershipTrackingPolicy { + @Override + protected void onEntityChange(Entity member) { + } + + @Override + protected void onEntityAdded(Entity member) { + if (member.getAttribute(HazelcastNode.NODE_NAME) == null) { + ((EntityInternal) member).setAttribute(HazelcastNode.NODE_NAME, "hazelcast-" + nextMemberId.incrementAndGet()); + if (LOG.isInfoEnabled()) { + LOG.info("Node {} added to the cluster", member); + } + } + } + + @Override + protected void onEntityRemoved(Entity member) { + } + }; + + @Override + public String getClusterName() { + return getConfig(CLUSTER_NAME); + } + + @Override + public String getClusterPassword() { + return getConfig(CLUSTER_PASSWORD); + } + + @Override + protected void initEnrichers() { + super.initEnrichers(); + + } + + @Override + public void start(Collection<? extends Location> locations) { + super.start(locations); + + + List<String> clusterNodes = Lists.newArrayList(); + for (Entity member : getMembers()) { + clusterNodes.add(member.getAttribute(Attributes.ADDRESS)); + } + setAttribute(PUBLIC_CLUSTER_NODES, clusterNodes); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNode.java ---------------------------------------------------------------------- diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNode.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNode.java new file mode 100644 index 0000000..076b6f5 --- /dev/null +++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNode.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package brooklyn.entity.nosql.hazelcast; + +import brooklyn.catalog.Catalog; +import brooklyn.config.ConfigKey; +import brooklyn.entity.basic.ConfigKeys; +import brooklyn.entity.basic.SoftwareProcess; +import brooklyn.entity.java.UsesJava; +import brooklyn.entity.java.UsesJmx; +import brooklyn.entity.proxying.ImplementedBy; +import brooklyn.event.basic.BasicAttributeSensorAndConfigKey; +import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSensorAndConfigKey; +import brooklyn.event.basic.PortAttributeSensorAndConfigKey; +import brooklyn.location.basic.PortRanges; +import brooklyn.util.flags.SetFromFlag; +import brooklyn.util.javalang.JavaClassNames; + +/** + * An {@link brooklyn.entity.Entity} that represents an Hazelcast node + */ +@Catalog(name="Hazelcast Node", description="Hazelcast is a clustering and highly scalable data distribution platform for Java.") + +@ImplementedBy(HazelcastNodeImpl.class) +public interface HazelcastNode extends SoftwareProcess, UsesJava, UsesJmx { + @SetFromFlag("version") + ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "3.4.2"); + + @SetFromFlag("downloadUrl") + BasicAttributeSensorAndConfigKey<String> DOWNLOAD_URL = new BasicAttributeSensorAndConfigKey<String>( + SoftwareProcess.DOWNLOAD_URL, "https://repo1.maven.org/maven2/com/hazelcast/hazelcast/${version}/hazelcast-${version}.jar"); + + @SetFromFlag("configTemplateUrl") + ConfigKey<String> CONFIG_TEMPLATE_URL = ConfigKeys.newStringConfigKey( + "hazelcast.node.config.templateUrl", "Template file (in freemarker format) for the Hazelcat config file", + JavaClassNames.resolveClasspathUrl(HazelcastNode.class, "hazelcast-brooklyn.xml")); + + @SetFromFlag("configFileName") + ConfigKey<String> CONFIG_FILE_NAME = ConfigKeys.newStringConfigKey( + "hazelcast.node.config.fileName", "Name of the Hazelcast config file", "hazelcast.xml"); + + @SetFromFlag("nodeName") + StringAttributeSensorAndConfigKey NODE_NAME = new StringAttributeSensorAndConfigKey("hazelcast.node.name", + "Node name (or randomly selected if not set", null); + + @SetFromFlag("nodeHeapMemorySize") + ConfigKey<String> NODE_HEAP_MEMORY_SIZE = ConfigKeys.newStringConfigKey( + "hazelcast.node.heap.memory.size", "Node's heap memory size (-Xmx and -Xms) in megabytes. Default: 256m", "256m"); + + @SetFromFlag("nodePort") + PortAttributeSensorAndConfigKey NODE_PORT = new PortAttributeSensorAndConfigKey("hazelcast.node.port", "Hazelcast communication port", PortRanges.fromString("5701+")); + + /** + * Specifies the group name in the configuration file. Each Hazelcast cluster has a separate group. + */ + @SetFromFlag("groupName") + ConfigKey<String> GROUP_NAME = ConfigKeys.newStringConfigKey("hazelcast.group.name", + "Group name", "brooklyn"); + + @SetFromFlag("groupPassword") + ConfigKey<String> GROUP_PASSWORD = ConfigKeys.newStringConfigKey("hazelcast.group.password", + "Group password", "brooklyn"); + + String getNodeName(); + + Integer getNodePort(); + + String getGroupName(); + + String getGroupPassword(); + + String getHostname(); + + String getHostAddress(); + + String getPrivateIpAddress(); + + String getListenAddress(); + + String getHeapMemorySize(); +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java ---------------------------------------------------------------------- diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java new file mode 100644 index 0000000..4e53add --- /dev/null +++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeDriver.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package brooklyn.entity.nosql.hazelcast; + +import brooklyn.entity.basic.SoftwareProcessDriver; + +public interface HazelcastNodeDriver extends SoftwareProcessDriver { + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java ---------------------------------------------------------------------- diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java new file mode 100644 index 0000000..0369934 --- /dev/null +++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeImpl.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package brooklyn.entity.nosql.hazelcast; + +import java.net.URI; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.entity.basic.Attributes; +import brooklyn.entity.basic.SoftwareProcessImpl; +import brooklyn.event.feed.http.HttpFeed; +import brooklyn.event.feed.http.HttpPollConfig; +import brooklyn.event.feed.http.HttpValueFunctions; +import brooklyn.location.access.BrooklynAccessUtils; +import brooklyn.util.text.Strings; + +import com.google.common.base.Functions; +import com.google.common.net.HostAndPort; + +public class HazelcastNodeImpl extends SoftwareProcessImpl implements HazelcastNode { + + private static final Logger LOG = LoggerFactory.getLogger(HazelcastNodeImpl.class); + + HttpFeed httpFeed; + + @Override + public Class<HazelcastNodeDriver> getDriverInterface() { + return HazelcastNodeDriver.class; + } + + @Override + protected void connectSensors() { + super.connectSensors(); + + if (LOG.isDebugEnabled()) { + LOG.debug("Connecting sensors for node: {} ", getAttribute(Attributes.HOSTNAME)); + } + + HostAndPort hp = BrooklynAccessUtils.getBrooklynAccessibleAddress(this, getNodePort()); + + String nodeUri = String.format("http://%s:%d/hazelcast/rest/cluster", hp.getHostText(), hp.getPort()); + setAttribute(Attributes.MAIN_URI, URI.create(nodeUri)); + + if (LOG.isDebugEnabled()) { + LOG.debug("Node {} is using {} as a main URI", this, nodeUri); + } + + httpFeed = HttpFeed.builder() + .entity(this) + .period(3000, TimeUnit.MILLISECONDS) + .baseUri(nodeUri) + .poll(new HttpPollConfig<Boolean>(SERVICE_UP) + .onSuccess(HttpValueFunctions.responseCodeEquals(200)) + .onFailureOrException(Functions.constant(false))) + .build(); + } + + @Override + protected void disconnectSensors() { + if (httpFeed != null) { + httpFeed.stop(); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("Disconnecting sensors for node: {} ", getAttribute(Attributes.HOSTNAME)); + } + + super.disconnectSensors(); + disconnectServiceUpIsRunning(); + } + + + @Override + public String getGroupName() { + return getConfig(HazelcastNode.GROUP_NAME); + } + + @Override + public String getGroupPassword() { + return getConfig(HazelcastNode.GROUP_PASSWORD); + } + + @Override + public String getNodeName() { + return getAttribute(HazelcastNode.NODE_NAME); + } + + @Override + public Integer getNodePort() { + return getAttribute(HazelcastNode.NODE_PORT); + } + + @Override + public String getHostname() { + return getAttribute(HOSTNAME); + } + + @Override + public String getHostAddress() { + return getAttribute(ADDRESS); + } + + @Override + public String getPrivateIpAddress() { + return getAttribute(SUBNET_ADDRESS); + } + + @Override + public String getListenAddress() { + String listenAddress = getPrivateIpAddress(); + + if (Strings.isBlank(listenAddress)) { + listenAddress = getAttribute(ADDRESS); + } + + if (LOG.isInfoEnabled()) { + LOG.info("Node {} is listening on {}", this, listenAddress); + } + + + return listenAddress; + } + + + @Override + public String getHeapMemorySize() { + return getConfig(HazelcastNode.NODE_HEAP_MEMORY_SIZE); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java ---------------------------------------------------------------------- diff --git a/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java new file mode 100644 index 0000000..5527a69 --- /dev/null +++ b/sandbox/nosql/src/main/java/brooklyn/entity/nosql/hazelcast/HazelcastNodeSshDriver.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package brooklyn.entity.nosql.hazelcast; + +import static java.lang.String.format; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.entity.Entity; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.basic.EntityLocal; +import brooklyn.entity.java.JavaSoftwareProcessSshDriver; +import brooklyn.location.basic.SshMachineLocation; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.os.Os; +import brooklyn.util.ssh.BashCommands; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +public class HazelcastNodeSshDriver extends JavaSoftwareProcessSshDriver implements HazelcastNodeDriver { + + private static final Logger LOG = LoggerFactory.getLogger(HazelcastNodeSshDriver.class); + + public HazelcastNodeSshDriver(EntityLocal entity, SshMachineLocation machine) { + super(entity, machine); + } + + @Override + public void preInstall() { + resolver = Entities.newDownloader(this); + } + + @Override + public void install() { + List<String> urls = resolver.getTargets(); + String saveAs = resolver.getFilename(); + + List<String> commands = ImmutableList.<String>builder() + .add(BashCommands.installJavaLatestOrWarn()) + .addAll(BashCommands.commandsToDownloadUrlsAs(urls, saveAs)) + .build(); + + newScript(INSTALLING).body.append(commands).execute(); + } + + @Override + public void customize() { + if (LOG.isInfoEnabled()) { + LOG.info("Customizing {}", entity.getAttribute(HazelcastNode.NODE_NAME)); + } + + ImmutableList.Builder<String> commands = new ImmutableList.Builder<String>() + .add("mkdir -p lib conf log") + .add(String.format("cp %s/%s %s/lib/", getInstallDir(), resolver.getFilename(), getRunDir())); + + newScript(CUSTOMIZING) + .body.append(commands.build()) + .failOnNonZeroResultCode() + .execute(); + + copyTemplate(entity.getConfig(HazelcastNode.CONFIG_TEMPLATE_URL), Os.mergePathsUnix(getRunDir(), "conf", getConfigFileName())); + + } + + @Override + public void launch() { + + entity.setAttribute(HazelcastNode.PID_FILE, Os.mergePathsUnix(getRunDir(), PID_FILENAME)); + + String maxHeapMemorySize = getHeapMemorySize(); + + if (LOG.isInfoEnabled()) { + LOG.info("Launching {} with heap memory of {}", entity, maxHeapMemorySize); + } + + // Setting initial heap size (Xms) size to match max heap size (Xms) at first + String initialHeapMemorySize = maxHeapMemorySize; + + StringBuilder commandBuilder = new StringBuilder() + .append(format("nohup java -cp ./lib/%s", resolver.getFilename())) + .append(format(" -Xmx%s -Xms%s", maxHeapMemorySize, initialHeapMemorySize)) + .append(format(" -Dhazelcast.config=./conf/%s", getConfigFileName())) + .append(format(" com.hazelcast.core.server.StartServer >> %s 2>&1 </dev/null &", getLogFileLocation())); + + newScript(MutableMap.of(USE_PID_FILE, true), LAUNCHING) + .updateTaskAndFailOnNonZeroResultCode() + .body.append(commandBuilder.toString()) + .execute(); + } + + public String getConfigFileName() { + return entity.getConfig(HazelcastNode.CONFIG_FILE_NAME); + } + + public String getHeapMemorySize() { + return entity.getConfig(HazelcastNode.NODE_HEAP_MEMORY_SIZE); + } + + @Override + public boolean isRunning() { + return newScript(MutableMap.of(USE_PID_FILE, true), CHECK_RUNNING).execute() == 0; + } + + @Override + public void stop() { + newScript(MutableMap.of(USE_PID_FILE, true), STOPPING).execute(); + } + + @Override + public void kill() { + newScript(MutableMap.of(USE_PID_FILE, true), KILLING).execute(); + } + + public List<String> getHazelcastNodesList() throws ExecutionException, InterruptedException { + HazelcastCluster cluster = (HazelcastCluster) entity.getParent(); + List<String> result = Lists.newArrayList(); + + for (Entity member : cluster.getMembers()) { + String address = Entities.attributeSupplierWhenReady(member, HazelcastNode.SUBNET_ADDRESS).get(); + Integer port = Entities.attributeSupplierWhenReady(member, HazelcastNode.NODE_PORT).get(); + + String addressAndPort = String.format("%s:%d", address, port); + + if (LOG.isInfoEnabled()) { + LOG.info("Adding {} to the members' list of {}", addressAndPort, entity.getAttribute(HazelcastNode.NODE_NAME)); + } + result.add(addressAndPort); + } + + return result; + } + + @Override + protected String getLogFileLocation() { + return Os.mergePathsUnix(getRunDir(),"/log/out.log"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/main/resources/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml ---------------------------------------------------------------------- diff --git a/sandbox/nosql/src/main/resources/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml b/sandbox/nosql/src/main/resources/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml new file mode 100644 index 0000000..459be4e --- /dev/null +++ b/sandbox/nosql/src/main/resources/brooklyn/entity/nosql/hazelcast/hazelcast-brooklyn.xml @@ -0,0 +1,65 @@ +[#ftl] +<?xml version="1.0" encoding="UTF-8"?> + +<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.4.xsd" + xmlns="http://www.hazelcast.com/schema/config" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <group> + <name>${entity.groupName}</name> + <password>${entity.groupPassword}</password> + </group> + <management-center enabled="false">http://localhost:8080/mancenter</management-center> + <network> + <port auto-increment="true" port-count="100">${entity.nodePort?c}</port> + <outbound-ports> + <!-- + Allowed port range when connecting to other nodes. + 0 or * means use system provided port. + --> + <ports>0</ports> + </outbound-ports> + + <interfaces enabled="true"> + <interface>${entity.listenAddress}</interface> + </interfaces> + + <join> + <multicast enabled="false" /> + + <tcp-ip enabled="true"> + [#list driver.hazelcastNodesList as member] + <member>${member}</member> + [/#list] + </tcp-ip> + + <aws enabled="false" /> + + </join> + + <ssl enabled="false"/> + <socket-interceptor enabled="false"/> + <symmetric-encryption enabled="false"/> + + </network> + <partition-group enabled="false"/> + + <map name="default"> + <in-memory-format>BINARY</in-memory-format> + <backup-count>1</backup-count> + <async-backup-count>0</async-backup-count> + <time-to-live-seconds>0</time-to-live-seconds> + <max-idle-seconds>0</max-idle-seconds> + <eviction-policy>NONE</eviction-policy> + <max-size policy="PER_NODE">0</max-size> + <eviction-percentage>25</eviction-percentage> + <min-eviction-check-millis>100</min-eviction-check-millis> + <merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy</merge-policy> + </map> + + <serialization> + <portable-version>0</portable-version> + </serialization> + + <services enable-defaults="true"/> + +</hazelcast> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java ---------------------------------------------------------------------- diff --git a/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java b/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java new file mode 100644 index 0000000..bce319c --- /dev/null +++ b/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterEc2LiveTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package brooklyn.entity.nosql.hazelcast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import brooklyn.entity.AbstractEc2LiveTest; +import brooklyn.entity.basic.Attributes; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.location.Location; +import brooklyn.test.EntityTestUtils; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; + +public class HazelcastClusterEc2LiveTest extends AbstractEc2LiveTest { + @SuppressWarnings("unused") + private static final Logger LOG = LoggerFactory.getLogger(HazelcastClusterEc2LiveTest.class); + + @Override + protected void doTest(Location loc) throws Exception { + HazelcastCluster cluster = app.createAndManageChild(EntitySpec.create(HazelcastCluster.class) + .configure(HazelcastCluster.INITIAL_SIZE, 3) + .configure(HazelcastCluster.MEMBER_SPEC, EntitySpec.create(HazelcastNode.class))); + app.start(ImmutableList.of(loc)); + + EntityTestUtils.assertAttributeEqualsEventually(cluster, HazelcastNode.SERVICE_UP, true); + + HazelcastNode first = (HazelcastNode) Iterables.get(cluster.getMembers(), 0); + HazelcastNode second = (HazelcastNode) Iterables.get(cluster.getMembers(), 1); + + assertNodesUpAndInCluster(first, second); + + EntityTestUtils.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_UP, true); + } + + private void assertNodesUpAndInCluster(final HazelcastNode... nodes) { + for (final HazelcastNode node : nodes) { + EntityTestUtils.assertAttributeEqualsEventually(node, HazelcastNode.SERVICE_UP, true); + } + } + + @Test(enabled = false) + public void testDummy() { + } // Convince TestNG IDE integration that this really does have test methods + + + @Test(groups = {"Live", "Live-sanity"}) + @Override + public void test_CentOS_6_3() throws Exception { + super.test_CentOS_6_3(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e37f0585/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java ---------------------------------------------------------------------- diff --git a/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java b/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java new file mode 100644 index 0000000..4ff4ab9 --- /dev/null +++ b/sandbox/nosql/src/test/java/brooklyn/entity/nosql/hazelcast/HazelcastClusterSoftlayerLiveTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package brooklyn.entity.nosql.hazelcast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import brooklyn.entity.AbstractSoftlayerLiveTest; +import brooklyn.entity.basic.Attributes; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.location.Location; +import brooklyn.test.EntityTestUtils; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; + +public class HazelcastClusterSoftlayerLiveTest extends AbstractSoftlayerLiveTest { + @SuppressWarnings("unused") + private static final Logger LOG = LoggerFactory.getLogger(HazelcastClusterSoftlayerLiveTest.class); + + @Override + protected void doTest(Location loc) throws Exception { + HazelcastCluster cluster = app.createAndManageChild(EntitySpec.create(HazelcastCluster.class) + .configure(HazelcastCluster.INITIAL_SIZE, 3) + .configure(HazelcastCluster.MEMBER_SPEC, EntitySpec.create(HazelcastNode.class))); + app.start(ImmutableList.of(loc)); + + EntityTestUtils.assertAttributeEqualsEventually(cluster, HazelcastNode.SERVICE_UP, true); + + HazelcastNode first = (HazelcastNode) Iterables.get(cluster.getMembers(), 0); + HazelcastNode second = (HazelcastNode) Iterables.get(cluster.getMembers(), 1); + + assertNodesUpAndInCluster(first, second); + + EntityTestUtils.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_UP, true); + } + + private void assertNodesUpAndInCluster(final HazelcastNode... nodes) { + for (final HazelcastNode node : nodes) { + EntityTestUtils.assertAttributeEqualsEventually(node, HazelcastNode.SERVICE_UP, true); + } + } + + @Test(enabled = false) + public void testDummy() { + } // Convince TestNG IDE integration that this really does have test methods + + + @Test(groups = {"Live", "Live-sanity"}) + @Override + public void test_Ubuntu_12_0_4() throws Exception { + super.test_Ubuntu_12_0_4(); + } +}
