http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlClusterImpl.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlClusterImpl.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlClusterImpl.java deleted file mode 100644 index f86ad43..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlClusterImpl.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * 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.database.mysql; - -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.basic.EntityLocal; -import org.apache.brooklyn.api.entity.proxying.EntitySpec; -import org.apache.brooklyn.api.event.AttributeSensor; -import org.apache.brooklyn.api.event.SensorEvent; -import org.apache.brooklyn.api.event.SensorEventListener; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.core.util.task.DynamicTasks; -import org.apache.brooklyn.core.util.task.TaskBuilder; - -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -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.reflect.TypeToken; - -import brooklyn.config.ConfigKey; -import brooklyn.enricher.Enrichers; -import brooklyn.entity.basic.Attributes; -import brooklyn.entity.basic.EntityInternal; -import brooklyn.entity.basic.EntityPredicates; -import brooklyn.entity.basic.ServiceStateLogic.ServiceNotUpLogic; -import brooklyn.entity.group.DynamicClusterImpl; -import brooklyn.event.basic.DependentConfiguration; -import brooklyn.event.basic.Sensors; -import brooklyn.event.feed.function.FunctionFeed; -import brooklyn.event.feed.function.FunctionPollConfig; -import brooklyn.util.collections.CollectionFunctionals; -import brooklyn.util.guava.Functionals; -import brooklyn.util.guava.IfFunctions; -import brooklyn.util.text.Identifiers; -import brooklyn.util.text.StringPredicates; -import brooklyn.util.time.Duration; - -// https://dev.mysql.com/doc/refman/5.7/en/replication-howto.html - -// TODO CREATION_SCRIPT_CONTENTS executed before replication setup so it is not replicated to slaves -// TODO Bootstrap slave from dump for the case where the binary log is purged -// TODO Promote slave to master -// TODO SSL connection between master and slave -// TODO DB credentials littered all over the place in file system -public class MySqlClusterImpl extends DynamicClusterImpl implements MySqlCluster { - private static final AttributeSensor<Boolean> NODE_REPLICATION_INITIALIZED = Sensors.newBooleanSensor("mysql.replication_initialized"); - - private static final String MASTER_CONFIG_URL = "classpath:///brooklyn/entity/database/mysql/mysql_master.conf"; - private static final String SLAVE_CONFIG_URL = "classpath:///brooklyn/entity/database/mysql/mysql_slave.conf"; - private static final int MASTER_SERVER_ID = 1; - private static final Predicate<Entity> IS_MASTER = EntityPredicates.configEqualTo(MySqlNode.MYSQL_SERVER_ID, MASTER_SERVER_ID); - - @SuppressWarnings("serial") - private static final AttributeSensor<Supplier<Integer>> SLAVE_NEXT_SERVER_ID = Sensors.newSensor(new TypeToken<Supplier<Integer>>() {}, - "mysql.slave.next_server_id", "Returns the ID of the next slave server"); - @SuppressWarnings("serial") - private static final AttributeSensor<Map<String, String>> SLAVE_ID_ADDRESS_MAPPING = Sensors.newSensor(new TypeToken<Map<String, String>>() {}, - "mysql.slave.id_address_mapping", "Maps slave entity IDs to SUBNET_ADDRESS, so the address is known at member remove time."); - - @Override - public void init() { - super.init(); - // Set id supplier in attribute so it is serialized - setAttribute(SLAVE_NEXT_SERVER_ID, new NextServerIdSupplier()); - setAttribute(SLAVE_ID_ADDRESS_MAPPING, new ConcurrentHashMap<String, String>()); - if (getConfig(SLAVE_PASSWORD) == null) { - setAttribute(SLAVE_PASSWORD, Identifiers.makeRandomId(8)); - } else { - setAttribute(SLAVE_PASSWORD, getConfig(SLAVE_PASSWORD)); - } - initSubscriptions(); - } - - @Override - public void rebind() { - super.rebind(); - initSubscriptions(); - } - - private void initSubscriptions() { - subscribeToMembers(this, MySqlNode.SERVICE_PROCESS_IS_RUNNING, new NodeRunningListener(this)); - subscribe(this, MEMBER_REMOVED, new MemberRemovedListener()); - } - - @Override - protected void initEnrichers() { - super.initEnrichers(); - propagateMasterAttribute(MySqlNode.HOSTNAME); - propagateMasterAttribute(MySqlNode.ADDRESS); - propagateMasterAttribute(MySqlNode.SUBNET_HOSTNAME); - propagateMasterAttribute(MySqlNode.SUBNET_ADDRESS); - propagateMasterAttribute(MySqlNode.MYSQL_PORT); - propagateMasterAttribute(MySqlNode.DATASTORE_URL); - - addEnricher(Enrichers.builder() - .aggregating(MySqlNode.DATASTORE_URL) - .publishing(SLAVE_DATASTORE_URL_LIST) - .computing(Functions.<Collection<String>>identity()) - .entityFilter(Predicates.not(IS_MASTER)) - .fromMembers() - .build()); - - addEnricher(Enrichers.builder() - .aggregating(MySqlNode.QUERIES_PER_SECOND_FROM_MYSQL) - .publishing(QUERIES_PER_SECOND_FROM_MYSQL_PER_NODE) - .fromMembers() - .computingAverage() - .defaultValueForUnreportedSensors(0d) - .build()); - } - - private void propagateMasterAttribute(AttributeSensor<?> att) { - addEnricher(Enrichers.builder() - .aggregating(att) - .publishing(att) - .computing(IfFunctions.ifPredicate(CollectionFunctionals.notEmpty()) - .apply(CollectionFunctionals.firstElement()) - .defaultValue(null)) - .entityFilter(IS_MASTER) - .build()); - } - - @Override - protected EntitySpec<?> getFirstMemberSpec() { - final EntitySpec<?> firstMemberSpec = super.getFirstMemberSpec(); - if (firstMemberSpec != null) { - return applyDefaults(firstMemberSpec, Suppliers.ofInstance(MASTER_SERVER_ID), MASTER_CONFIG_URL, false); - } - - final EntitySpec<?> memberSpec = super.getMemberSpec(); - if (memberSpec != null) { - if (!isKeyConfigured(memberSpec, MySqlNode.TEMPLATE_CONFIGURATION_URL.getConfigKey())) { - return EntitySpec.create(memberSpec) - .configure(MySqlNode.MYSQL_SERVER_ID, MASTER_SERVER_ID) - .configure(MySqlNode.TEMPLATE_CONFIGURATION_URL, MASTER_CONFIG_URL); - } else { - return memberSpec; - } - } - - return EntitySpec.create(MySqlNode.class) - .displayName("MySql Master") - .configure(MySqlNode.MYSQL_SERVER_ID, MASTER_SERVER_ID) - .configure(MySqlNode.TEMPLATE_CONFIGURATION_URL, MASTER_CONFIG_URL); - } - - @Override - protected EntitySpec<?> getMemberSpec() { - Supplier<Integer> serverIdSupplier = getAttribute(SLAVE_NEXT_SERVER_ID); - - EntitySpec<?> spec = super.getMemberSpec(); - if (spec != null) { - return applyDefaults(spec, serverIdSupplier, SLAVE_CONFIG_URL, true); - } - - return EntitySpec.create(MySqlNode.class) - .displayName("MySql Slave") - .configure(MySqlNode.MYSQL_SERVER_ID, serverIdSupplier.get()) - .configure(MySqlNode.TEMPLATE_CONFIGURATION_URL, SLAVE_CONFIG_URL) - // block inheritance, only master should execute the creation script - .configure(MySqlNode.CREATION_SCRIPT_URL, (String) null) - .configure(MySqlNode.CREATION_SCRIPT_CONTENTS, (String) null); - } - - private EntitySpec<?> applyDefaults(EntitySpec<?> spec, Supplier<Integer> serverId, String configUrl, boolean resetCreationScript) { - boolean needsServerId = !isKeyConfigured(spec, MySqlNode.MYSQL_SERVER_ID); - boolean needsConfigUrl = !isKeyConfigured(spec, MySqlNode.TEMPLATE_CONFIGURATION_URL.getConfigKey()); - boolean needsCreationScriptUrl = resetCreationScript && !isKeyConfigured(spec, MySqlNode.CREATION_SCRIPT_URL); - boolean needsCreationScriptContents = resetCreationScript && !isKeyConfigured(spec, MySqlNode.CREATION_SCRIPT_CONTENTS); - if (needsServerId || needsConfigUrl || needsCreationScriptUrl || needsCreationScriptContents) { - EntitySpec<?> clonedSpec = EntitySpec.create(spec); - if (needsServerId) { - clonedSpec.configure(MySqlNode.MYSQL_SERVER_ID, serverId.get()); - } - if (needsConfigUrl) { - clonedSpec.configure(MySqlNode.TEMPLATE_CONFIGURATION_URL, configUrl); - } - if (needsCreationScriptUrl) { - clonedSpec.configure(MySqlNode.CREATION_SCRIPT_URL, (String) null); - } - if (needsCreationScriptContents) { - clonedSpec.configure(MySqlNode.CREATION_SCRIPT_URL, (String) null); - } - return clonedSpec; - } else { - return spec; - } - } - - private boolean isKeyConfigured(EntitySpec<?> spec, ConfigKey<?> key) { - return spec.getConfig().containsKey(key) || spec.getFlags().containsKey(key.getName()); - } - - @Override - protected Entity createNode(Location loc, Map<?, ?> flags) { - Entity node = super.createNode(loc, flags); - if (!IS_MASTER.apply(node)) { - ServiceNotUpLogic.updateNotUpIndicator((EntityLocal)node, MySqlSlave.SLAVE_HEALTHY, "Replication not started"); - - addFeed(FunctionFeed.builder() - .entity((EntityLocal)node) - .period(Duration.FIVE_SECONDS) - .poll(FunctionPollConfig.forSensor(MySqlSlave.SLAVE_HEALTHY) - .callable(new SlaveStateCallable(node)) - .checkSuccess(StringPredicates.isNonBlank()) - .onSuccess(new SlaveStateParser(node)) - .setOnFailure(false) - .description("Polls SHOW SLAVE STATUS")) - .build()); - - node.addEnricher(Enrichers.builder().updatingMap(Attributes.SERVICE_NOT_UP_INDICATORS) - .from(MySqlSlave.SLAVE_HEALTHY) - .computing(Functionals.ifNotEquals(true).value("Slave replication status is not healthy") ) - .build()); - } - return node; - } - - public static class SlaveStateCallable implements Callable<String> { - private Entity slave; - public SlaveStateCallable(Entity slave) { - this.slave = slave; - } - - @Override - public String call() throws Exception { - if (Boolean.TRUE.equals(slave.getAttribute(MySqlNode.SERVICE_PROCESS_IS_RUNNING))) { - return slave.invoke(MySqlNode.EXECUTE_SCRIPT, ImmutableMap.of("commands", "SHOW SLAVE STATUS \\G")).asTask().getUnchecked(); - } else { - return null; - } - } - - } - - public static class SlaveStateParser implements Function<String, Boolean> { - private Entity slave; - - public SlaveStateParser(Entity slave) { - this.slave = slave; - } - - @Override - public Boolean apply(String result) { - Map<String, String> status = MySqlRowParser.parseSingle(result); - String secondsBehindMaster = status.get("Seconds_Behind_Master"); - if (secondsBehindMaster != null && !"NULL".equals(secondsBehindMaster)) { - ((EntityLocal)slave).setAttribute(MySqlSlave.SLAVE_SECONDS_BEHIND_MASTER, new Integer(secondsBehindMaster)); - } - return "Yes".equals(status.get("Slave_IO_Running")) && "Yes".equals(status.get("Slave_SQL_Running")); - } - - } - - private static class NextServerIdSupplier implements Supplier<Integer> { - private AtomicInteger nextId = new AtomicInteger(MASTER_SERVER_ID+1); - - @Override - public Integer get() { - return nextId.getAndIncrement(); - } - } - - // ============= Member Init ============= - - // The task is executed in inessential context (event handler) so - // not visible in tasks UI. Better make it visible so the user can - // see failures, currently accessible only from logs. - private static final class InitReplicationTask implements Runnable { - private final MySqlCluster cluster; - private final MySqlNode node; - - private InitReplicationTask(MySqlCluster cluster, MySqlNode node) { - this.cluster = cluster; - this.node = node; - } - - @Override - public void run() { - Integer serverId = node.getConfig(MySqlNode.MYSQL_SERVER_ID); - if (serverId == MASTER_SERVER_ID) { - initMaster(node); - } else if (serverId > MASTER_SERVER_ID) { - initSlave(node); - } - } - - private void initMaster(MySqlNode master) { - String binLogInfo = executeScriptOnNode(master, "FLUSH TABLES WITH READ LOCK;SHOW MASTER STATUS \\G UNLOCK TABLES;"); - Map<String, String> status = MySqlRowParser.parseSingle(binLogInfo); - String file = status.get("File"); - if (file != null) { - ((EntityInternal)master).setAttribute(MySqlMaster.MASTER_LOG_FILE, file); - } - String position = status.get("Position"); - if (position != null) { - ((EntityInternal)master).setAttribute(MySqlMaster.MASTER_LOG_POSITION, new Integer(position)); - } - } - - private void initSlave(MySqlNode slave) { - MySqlNode master = (MySqlNode) Iterables.find(cluster.getMembers(), IS_MASTER); - String masterLogFile = validateSqlParam(getAttributeBlocking(master, MySqlMaster.MASTER_LOG_FILE)); - Integer masterLogPos = getAttributeBlocking(master, MySqlMaster.MASTER_LOG_POSITION); - String masterAddress = validateSqlParam(master.getAttribute(MySqlNode.SUBNET_ADDRESS)); - Integer masterPort = master.getAttribute(MySqlNode.MYSQL_PORT); - String slaveAddress = validateSqlParam(slave.getAttribute(MySqlNode.SUBNET_ADDRESS)); - String username = validateSqlParam(cluster.getConfig(SLAVE_USERNAME)); - String password = validateSqlParam(cluster.getAttribute(SLAVE_PASSWORD)); - - executeScriptOnNode(master, String.format( - "CREATE USER '%s'@'%s' IDENTIFIED BY '%s';\n" + - "GRANT REPLICATION SLAVE ON *.* TO '%s'@'%s';\n", - username, slaveAddress, password, username, slaveAddress)); - - String slaveCmd = String.format( - "CHANGE MASTER TO " + - "MASTER_HOST='%s', " + - "MASTER_PORT=%d, " + - "MASTER_USER='%s', " + - "MASTER_PASSWORD='%s', " + - "MASTER_LOG_FILE='%s', " + - "MASTER_LOG_POS=%d;\n" + - "START SLAVE;\n", - masterAddress, masterPort, username, password, masterLogFile, masterLogPos); - executeScriptOnNode(slave, slaveCmd); - - cluster.getAttribute(SLAVE_ID_ADDRESS_MAPPING).put(slave.getId(), slave.getAttribute(MySqlNode.SUBNET_ADDRESS)); - } - - private <T> T getAttributeBlocking(Entity masterNode, AttributeSensor<T> att) { - return DynamicTasks.queue(DependentConfiguration.attributeWhenReady(masterNode, att)).getUnchecked(); - } - - } - - private static final class NodeRunningListener implements SensorEventListener<Boolean> { - private MySqlCluster cluster; - - public NodeRunningListener(MySqlCluster cluster) { - this.cluster = cluster; - } - - @Override - public void onEvent(SensorEvent<Boolean> event) { - final MySqlNode node = (MySqlNode) event.getSource(); - if (Boolean.TRUE.equals(event.getValue()) && - // We are interested in SERVICE_PROCESS_IS_RUNNING only while haven't come online yet. - // Probably will get several updates while replication is initialized so an additional - // check is needed whether we have already seen this. - Boolean.FALSE.equals(node.getAttribute(MySqlNode.SERVICE_UP)) && - !Boolean.TRUE.equals(node.getAttribute(NODE_REPLICATION_INITIALIZED))) { - - // Events executed sequentially so no need to synchronize here. - ((EntityLocal)node).setAttribute(NODE_REPLICATION_INITIALIZED, Boolean.TRUE); - - DynamicTasks.queueIfPossible(TaskBuilder.builder() - .name("Configure master-slave replication on node") - .body(new InitReplicationTask(cluster, node)) - .build()) - .orSubmitAsync(node); - } - } - - } - - // ============= Member Remove ============= - - public class MemberRemovedListener implements SensorEventListener<Entity> { - @Override - public void onEvent(SensorEvent<Entity> event) { - MySqlCluster cluster = (MySqlCluster) event.getSource(); - Entity node = event.getValue(); - String slaveAddress = cluster.getAttribute(SLAVE_ID_ADDRESS_MAPPING).remove(node.getId()); - if (slaveAddress != null) { - DynamicTasks.queueIfPossible(TaskBuilder.builder() - .name("Remove slave access") - .body(new RemoveSlaveConfigTask(cluster, slaveAddress)) - .build()) - .orSubmitAsync(cluster); - } - } - } - - public class RemoveSlaveConfigTask implements Runnable { - private MySqlCluster cluster; - private String slaveAddress; - - public RemoveSlaveConfigTask(MySqlCluster cluster, String slaveAddress) { - this.cluster = cluster; - this.slaveAddress = validateSqlParam(slaveAddress); - } - - @Override - public void run() { - // Could already be gone if stopping the entire app - let it throw an exception - MySqlNode master = (MySqlNode) Iterables.find(cluster.getMembers(), IS_MASTER); - String username = validateSqlParam(cluster.getConfig(SLAVE_USERNAME)); - executeScriptOnNode(master, String.format("DROP USER '%s'@'%s';", username, slaveAddress)); - } - - } - - // Can't call node.executeScript directly, need to change execution context, so use an effector task - private static String executeScriptOnNode(MySqlNode node, String commands) { - return node.invoke(MySqlNode.EXECUTE_SCRIPT, ImmutableMap.of(MySqlNode.EXECUTE_SCRIPT_COMMANDS, commands)).getUnchecked(); - } - - private static String validateSqlParam(String config) { - // Don't go into escape madness, just deny any suspicious strings. - // Would be nice to use prepared statements, but not worth pulling in the extra dependencies. - if (config.contains("'") && config.contains("\\")) { - throw new IllegalStateException("User provided string contains illegal SQL characters: " + config); - } - return config; - } - -}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlDriver.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlDriver.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlDriver.java deleted file mode 100644 index 2c90142..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlDriver.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.database.mysql; - -import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper; - -import brooklyn.entity.basic.SoftwareProcessDriver; - -/** - * The {@link SoftwareProcessDriver} for MySQL. - */ -public interface MySqlDriver extends SoftwareProcessDriver { - public String getStatusCmd(); - public ProcessTaskWrapper<Integer> executeScriptAsync(String commands); -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNode.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNode.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNode.java deleted file mode 100644 index 99b53c5..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNode.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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.database.mysql; - -import org.apache.brooklyn.api.catalog.Catalog; -import org.apache.brooklyn.api.entity.proxying.ImplementedBy; -import org.apache.brooklyn.api.entity.trait.HasShortName; -import org.apache.brooklyn.api.event.AttributeSensor; -import org.apache.brooklyn.core.util.flags.SetFromFlag; - -import brooklyn.config.ConfigKey; -import brooklyn.entity.annotation.Effector; -import brooklyn.entity.annotation.EffectorParam; -import brooklyn.entity.basic.Attributes; -import brooklyn.entity.basic.ConfigKeys; -import brooklyn.entity.basic.MethodEffector; -import brooklyn.entity.basic.SoftwareProcess; -import brooklyn.entity.database.DatastoreMixins.DatastoreCommon; -import brooklyn.event.basic.BasicAttributeSensorAndConfigKey; -import brooklyn.event.basic.BasicAttributeSensorAndConfigKey.StringAttributeSensorAndConfigKey; -import brooklyn.event.basic.MapConfigKey; -import brooklyn.event.basic.PortAttributeSensorAndConfigKey; -import brooklyn.event.basic.Sensors; - -import org.apache.brooklyn.location.basic.PortRanges; - -@Catalog(name="MySql Node", description="MySql is an open source relational database management system (RDBMS)", iconUrl="classpath:///mysql-logo-110x57.png") -@ImplementedBy(MySqlNodeImpl.class) -public interface MySqlNode extends SoftwareProcess, HasShortName, DatastoreCommon { - - // NOTE MySQL changes the minor version number of their GA release frequently, check for latest version if install fails - @SetFromFlag("version") - ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "5.6.26"); - - //http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.26-osx10.9-x86_64.tar.gz - //http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.26-linux-glibc2.5-x86_64.tar.gz - @SetFromFlag("downloadUrl") - BasicAttributeSensorAndConfigKey<String> DOWNLOAD_URL = new StringAttributeSensorAndConfigKey( - Attributes.DOWNLOAD_URL, "http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-${version}-${driver.osTag}.tar.gz"); - - @SetFromFlag("port") - PortAttributeSensorAndConfigKey MYSQL_PORT = new PortAttributeSensorAndConfigKey("mysql.port", "MySQL port", PortRanges.fromString("3306, 13306+")); - - @SetFromFlag("dataDir") - ConfigKey<String> DATA_DIR = ConfigKeys.newStringConfigKey( - "mysql.datadir", "Directory for writing data files", null); - - @SetFromFlag("serverConf") - MapConfigKey<Object> MYSQL_SERVER_CONF = new MapConfigKey<Object>( - Object.class, "mysql.server.conf", "Configuration options for mysqld"); - - ConfigKey<Object> MYSQL_SERVER_CONF_LOWER_CASE_TABLE_NAMES = MYSQL_SERVER_CONF.subKey("lower_case_table_names", "See MySQL guide. Set 1 to ignore case in table names (useful for OS portability)"); - - @SetFromFlag("serverId") - ConfigKey<Integer> MYSQL_SERVER_ID = ConfigKeys.newIntegerConfigKey("mysql.server_id", "Corresponds to server_id option", 0); - - @SetFromFlag("password") - StringAttributeSensorAndConfigKey PASSWORD = new StringAttributeSensorAndConfigKey( - "mysql.password", "Database admin password (or randomly generated if not set)", null); - - @SetFromFlag("socketUid") - StringAttributeSensorAndConfigKey SOCKET_UID = new StringAttributeSensorAndConfigKey( - "mysql.socketUid", "Socket uid, for use in file /tmp/mysql.sock.<uid>.3306 (or randomly generated if not set)", null); - - /** @deprecated since 0.7.0 use DATASTORE_URL */ @Deprecated - AttributeSensor<String> MYSQL_URL = DATASTORE_URL; - - @SetFromFlag("configurationTemplateUrl") - BasicAttributeSensorAndConfigKey<String> TEMPLATE_CONFIGURATION_URL = new StringAttributeSensorAndConfigKey( - "mysql.template.configuration.url", "Template file (in freemarker format) for the mysql.conf file", - "classpath://brooklyn/entity/database/mysql/mysql.conf"); - - AttributeSensor<Double> QUERIES_PER_SECOND_FROM_MYSQL = Sensors.newDoubleSensor("mysql.queries.perSec.fromMysql"); - - MethodEffector<String> EXECUTE_SCRIPT = new MethodEffector<String>(MySqlNode.class, "executeScript"); - String EXECUTE_SCRIPT_COMMANDS = "commands"; - - @Effector(description = "Execute SQL script on the node as the root user") - String executeScript(@EffectorParam(name=EXECUTE_SCRIPT_COMMANDS) String commands); - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNodeImpl.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNodeImpl.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNodeImpl.java deleted file mode 100644 index dfbcbf1..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlNodeImpl.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.database.mysql; - -import java.util.Map; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.core.util.config.ConfigBag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.entity.basic.SoftwareProcessImpl; -import brooklyn.entity.effector.EffectorBody; -import brooklyn.event.feed.ssh.SshFeed; -import brooklyn.event.feed.ssh.SshPollConfig; -import brooklyn.event.feed.ssh.SshPollValue; - -import org.apache.brooklyn.location.basic.Locations; -import org.apache.brooklyn.location.basic.SshMachineLocation; - -import brooklyn.util.collections.MutableMap; -import brooklyn.util.guava.Maybe; -import brooklyn.util.text.Identifiers; -import brooklyn.util.text.Strings; -import brooklyn.util.time.Duration; - -import com.google.common.base.Function; - -public class MySqlNodeImpl extends SoftwareProcessImpl implements MySqlNode { - - private static final Logger LOG = LoggerFactory.getLogger(MySqlNodeImpl.class); - - private SshFeed feed; - - public MySqlNodeImpl() { - } - - public MySqlNodeImpl(Entity parent) { - this(MutableMap.of(), parent); - } - - public MySqlNodeImpl(Map<?,?> flags) { - super(flags, null); - } - - public MySqlNodeImpl(Map<?,?> flags, Entity parent) { - super(flags, parent); - } - - @Override - public Class<?> getDriverInterface() { - return MySqlDriver.class; - } - - @Override - public MySqlDriver getDriver() { - return (MySqlDriver) super.getDriver(); - } - - @Override - public void init() { - super.init(); - getMutableEntityType().addEffector(EXECUTE_SCRIPT, new EffectorBody<String>() { - @Override - public String call(ConfigBag parameters) { - return executeScript((String)parameters.getStringKey("commands")); - } - }); - } - - @Override - protected void connectSensors() { - super.connectSensors(); - setAttribute(DATASTORE_URL, String.format("mysql://%s:%s/", getAttribute(HOSTNAME), getAttribute(MYSQL_PORT))); - - /* - * TODO status gives us things like: - * Uptime: 2427 Threads: 1 Questions: 581 Slow queries: 0 Opens: 53 Flush tables: 1 Open tables: 35 Queries per second avg: 0.239 - * So can extract lots of sensors from that. - */ - Maybe<SshMachineLocation> machine = Locations.findUniqueSshMachineLocation(getLocations()); - boolean retrieveUsageMetrics = getConfig(RETRIEVE_USAGE_METRICS); - - if (machine.isPresent()) { - String cmd = getDriver().getStatusCmd(); - feed = SshFeed.builder() - .entity(this) - .period(Duration.FIVE_SECONDS) - .machine(machine.get()) - .poll(new SshPollConfig<Double>(QUERIES_PER_SECOND_FROM_MYSQL) - .command(cmd) - .onSuccess(new Function<SshPollValue, Double>() { - @Override - public Double apply(SshPollValue input) { - String q = Strings.getFirstWordAfter(input.getStdout(), "Queries per second avg:"); - if (q==null) return null; - return Double.parseDouble(q); - }}) - .setOnFailureOrException(null) - .enabled(retrieveUsageMetrics)) - .poll(new SshPollConfig<Boolean>(SERVICE_PROCESS_IS_RUNNING) - .command(cmd) - .setOnSuccess(true) - .setOnFailureOrException(false) - .suppressDuplicates(true)) - .build(); - } else { - LOG.warn("Location(s) {} not an ssh-machine location, so not polling for status; setting serviceUp immediately", getLocations()); - setAttribute(SERVICE_UP, true); - } - } - - @Override - protected void disconnectSensors() { - if (feed != null) feed.stop(); - super.disconnectSensors(); - } - - public int getPort() { - return getAttribute(MYSQL_PORT); - } - - public String getSocketUid() { - String result = getAttribute(MySqlNode.SOCKET_UID); - if (Strings.isBlank(result)) { - result = Identifiers.makeRandomId(6); - setAttribute(MySqlNode.SOCKET_UID, result); - } - return result; - } - - public String getPassword() { - String result = getAttribute(MySqlNode.PASSWORD); - if (Strings.isBlank(result)) { - result = Identifiers.makeRandomId(6); - setAttribute(MySqlNode.PASSWORD, result); - } - return result; - } - - @Override - public String getShortName() { - return "MySQL"; - } - - @Override - public String executeScript(String commands) { - return getDriver().executeScriptAsync(commands).block().getStdout(); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlRowParser.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlRowParser.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlRowParser.java deleted file mode 100644 index cdd5149..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlRowParser.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.database.mysql; - -import java.util.Map; - -import brooklyn.util.collections.MutableMap; -import brooklyn.util.text.Strings; - -public class MySqlRowParser { - public static Map<String, String> parseSingle(String row) { - Map<String, String> values = MutableMap.of(); - String[] lines = row.split("\\n"); - for (String line : lines) { - if (line.startsWith("*")) continue; // row delimiter - String[] arr = line.split(":", 2); - String key = arr[0].trim(); - String value = Strings.emptyToNull(arr[1].trim()); - values.put(key, value); - } - return values; - }; -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlSshDriver.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlSshDriver.java b/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlSshDriver.java deleted file mode 100644 index 32b530d..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/mysql/MySqlSshDriver.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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.database.mysql; - -import static brooklyn.util.JavaGroovyEquivalents.groovyTruth; -import static brooklyn.util.ssh.BashCommands.commandsToDownloadUrlsAs; -import static brooklyn.util.ssh.BashCommands.installPackage; -import static java.lang.String.format; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver; -import brooklyn.entity.basic.Attributes; -import brooklyn.entity.basic.Entities; -import brooklyn.entity.database.DatastoreMixins; -import brooklyn.entity.software.SshEffectorTasks; - -import org.apache.brooklyn.api.location.OsDetails; -import org.apache.brooklyn.core.util.task.DynamicTasks; -import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper; -import org.apache.brooklyn.location.basic.BasicOsDetails.OsVersions; -import org.apache.brooklyn.location.basic.SshMachineLocation; - -import brooklyn.util.collections.MutableMap; -import brooklyn.util.exceptions.Exceptions; -import brooklyn.util.io.FileUtil; -import brooklyn.util.net.Urls; -import brooklyn.util.os.Os; -import brooklyn.util.ssh.BashCommands; -import brooklyn.util.stream.Streams; -import brooklyn.util.text.ComparableVersion; -import brooklyn.util.text.Identifiers; -import brooklyn.util.text.Strings; -import brooklyn.util.time.CountdownTimer; -import brooklyn.util.time.Duration; - -import com.google.common.collect.ImmutableMap; - -/** - * The SSH implementation of the {@link MySqlDriver}. - */ -public class MySqlSshDriver extends AbstractSoftwareProcessSshDriver implements MySqlDriver { - - public static final Logger log = LoggerFactory.getLogger(MySqlSshDriver.class); - - public MySqlSshDriver(MySqlNodeImpl entity, SshMachineLocation machine) { - super(entity, machine); - - entity.setAttribute(Attributes.LOG_FILE_LOCATION, getLogFile()); - } - - public String getOsTag() { - // e.g. "osx10.6-x86_64"; see http://www.mysql.com/downloads/mysql/#downloads - OsDetails os = getLocation().getOsDetails(); - if (os == null) return "linux-glibc2.5-x86_64"; - if (os.isMac()) { - String osp1 = os.getVersion()==null ? "osx10.8" //lowest common denominator - : new ComparableVersion(os.getVersion()).isGreaterThanOrEqualTo(OsVersions.MAC_10_9) ? "osx10.9" - : "osx10.8"; //lowest common denominator - if (!os.is64bit()) { - throw new IllegalStateException("Only 64 bit MySQL build is available for OS X"); - } - return osp1+"-x86_64"; - } - //assume generic linux - String osp1 = "linux-glibc2.5"; - String osp2 = os.is64bit() ? "x86_64" : "i686"; - return osp1+"-"+osp2; - } - - public String getBaseDir() { return getExpandedInstallDir(); } - - public String getDataDir() { - String result = entity.getConfig(MySqlNode.DATA_DIR); - return (result == null) ? "." : result; - } - - public String getLogFile() { - return Urls.mergePaths(getRunDir(), "console.log"); - } - - public String getConfigFile() { - return "mymysql.cnf"; - } - - public String getInstallFilename() { - return String.format("mysql-%s-%s.tar.gz", getVersion(), getOsTag()); - } - - @Override - public void preInstall() { - resolver = Entities.newDownloader(this, ImmutableMap.of("filename", getInstallFilename())); - setExpandedInstallDir(Os.mergePaths(getInstallDir(), resolver.getUnpackedDirectoryName(format("mysql-%s-%s", getVersion(), getOsTag())))); - } - - @Override - public void install() { - List<String> urls = resolver.getTargets(); - String saveAs = resolver.getFilename(); - - List<String> commands = new LinkedList<String>(); - commands.add(BashCommands.INSTALL_TAR); - commands.add(BashCommands.INSTALL_CURL); - - commands.add("echo installing extra packages"); - commands.add(installPackage(ImmutableMap.of("yum", "libgcc_s.so.1"), null)); - commands.add(installPackage(ImmutableMap.of("yum", "libaio.so.1 libncurses.so.5", "apt", "libaio1 libaio-dev"), null)); - - // these deps are only needed on some OS versions but others don't need them - commands.add(installPackage(ImmutableMap.of("yum", "libaio", "apt", "ia32-libs"), null)); - commands.add("echo finished installing extra packages"); - commands.addAll(commandsToDownloadUrlsAs(urls, saveAs)); - commands.add(format("tar xfvz %s", saveAs)); - - newScript(INSTALLING).body.append(commands).execute(); - } - - @Override - public MySqlNodeImpl getEntity() { return (MySqlNodeImpl) super.getEntity(); } - public int getPort() { return getEntity().getPort(); } - public String getSocketUid() { return getEntity().getSocketUid(); } - public String getPassword() { return getEntity().getPassword(); } - - @Override - public void customize() { - copyDatabaseConfigScript(); - - newScript(CUSTOMIZING) - .updateTaskAndFailOnNonZeroResultCode() - .body.append( - "chmod 600 "+getConfigFile(), - getBaseDir()+"/scripts/mysql_install_db "+ - "--basedir="+getBaseDir()+" --datadir="+getDataDir()+" "+ - "--defaults-file="+getConfigFile()) - .execute(); - - // launch, then we will configure it - launch(); - - CountdownTimer timer = Duration.seconds(20).countdownTimer(); - boolean hasCreationScript = copyDatabaseCreationScript(); - timer.waitForExpiryUnchecked(); - - DynamicTasks.queue( - SshEffectorTasks.ssh( - "cd "+getRunDir(), - getBaseDir()+"/bin/mysqladmin --defaults-file="+getConfigFile()+" --password= password "+getPassword() - ).summary("setting password")); - - if (hasCreationScript) - executeScriptFromInstalledFileAsync("creation-script.sql").asTask().getUnchecked(); - - // not sure necessary to stop then subsequently launch, but seems safest - // (if skipping, use a flag in launch to indicate we've just launched it) - stop(); - } - - protected void copyDatabaseConfigScript() { - newScript(CUSTOMIZING).execute(); //create the directory - - String configScriptContents = processTemplate(entity.getAttribute(MySqlNode.TEMPLATE_CONFIGURATION_URL)); - Reader configContents = new StringReader(configScriptContents); - - getMachine().copyTo(configContents, Urls.mergePaths(getRunDir(), getConfigFile())); - } - - protected boolean copyDatabaseCreationScript() { - String creationScriptContents = DatastoreMixins.getDatabaseCreationScriptAsString(entity); - if (creationScriptContents==null) return false; - - File templateFile = null; - BufferedWriter writer = null; - try { - templateFile = File.createTempFile("mysql", null); - FileUtil.setFilePermissionsTo600(templateFile); - writer = new BufferedWriter(new FileWriter(templateFile)); - writer.write(creationScriptContents); - writer.flush(); - copyTemplate(templateFile.getAbsoluteFile(), getRunDir() + "/creation-script.sql"); - } catch (IOException e) { - throw Exceptions.propagate(e); - } finally { - if (writer != null) Streams.closeQuietly(writer); - if (templateFile != null) templateFile.delete(); - } - return true; - } - - public String getMySqlServerOptionsString() { - Map<String, Object> options = entity.getConfig(MySqlNode.MYSQL_SERVER_CONF); - StringBuilder result = new StringBuilder(); - if (groovyTruth(options)) { - for (Map.Entry<String, Object> entry : options.entrySet()) { - result.append(entry.getKey()); - String value = entry.getValue().toString(); - if (!Strings.isEmpty(value)) { - result.append(" = ").append(value); - } - result.append('\n'); - } - } - return result.toString(); - } - - @Override - public void launch() { - entity.setAttribute(MySqlNode.PID_FILE, getRunDir() + "/" + AbstractSoftwareProcessSshDriver.PID_FILENAME); - newScript(MutableMap.of("usePidFile", true), LAUNCHING) - .updateTaskAndFailOnNonZeroResultCode() - .body.append(format("nohup %s/bin/mysqld --defaults-file=%s --user=`whoami` > %s 2>&1 < /dev/null &", getBaseDir(), getConfigFile(), getLogFile())) - .execute(); - } - - @Override - public boolean isRunning() { - return newScript(MutableMap.of("usePidFile", false), CHECK_RUNNING) - .body.append(getStatusCmd()) - .execute() == 0; - } - - @Override - public void stop() { - newScript(MutableMap.of("usePidFile", true), STOPPING).execute(); - } - - @Override - public void kill() { - newScript(MutableMap.of("usePidFile", true), KILLING).execute(); - } - - @Override - public String getStatusCmd() { - return format("%s/bin/mysqladmin --defaults-file=%s status", getBaseDir(), Urls.mergePaths(getRunDir(), getConfigFile())); - } - - @Override - public ProcessTaskWrapper<Integer> executeScriptAsync(String commands) { - String filename = "mysql-commands-"+Identifiers.makeRandomId(8); - DynamicTasks.queue(SshEffectorTasks.put(Urls.mergePaths(getRunDir(), filename)).contents(commands).summary("copying datastore script to execute "+filename)); - return executeScriptFromInstalledFileAsync(filename); - } - - public ProcessTaskWrapper<Integer> executeScriptFromInstalledFileAsync(String filenameAlreadyInstalledAtServer) { - return DynamicTasks.queue( - SshEffectorTasks.ssh( - "cd "+getRunDir(), - getBaseDir()+"/bin/mysql --defaults-file="+getConfigFile()+" < "+filenameAlreadyInstalledAtServer) - .requiringExitCodeZero() - .summary("executing datastore script "+filenameAlreadyInstalledAtServer)); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlDriver.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlDriver.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlDriver.java deleted file mode 100644 index 02ad039..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlDriver.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.database.postgresql; - -import org.apache.brooklyn.core.util.task.system.ProcessTaskWrapper; - -import brooklyn.entity.basic.SoftwareProcessDriver; - -/** - * The {@link brooklyn.entity.basic.SoftwareProcessDriver} for PostgreSQL. - */ -public interface PostgreSqlDriver extends SoftwareProcessDriver { - - String getStatusCmd(); - - ProcessTaskWrapper<Integer> executeScriptAsync(String commands); -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNode.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNode.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNode.java deleted file mode 100644 index 20f4b76..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNode.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.database.postgresql; - -import org.apache.brooklyn.api.catalog.Catalog; -import org.apache.brooklyn.api.entity.Effector; -import org.apache.brooklyn.api.entity.proxying.ImplementedBy; -import org.apache.brooklyn.api.entity.trait.HasShortName; -import org.apache.brooklyn.core.util.flags.SetFromFlag; - -import brooklyn.config.ConfigKey; -import brooklyn.entity.basic.ConfigKeys; -import brooklyn.entity.basic.SoftwareProcess; -import brooklyn.entity.database.DatabaseNode; -import brooklyn.entity.database.DatastoreMixins; -import brooklyn.entity.database.DatastoreMixins.DatastoreCommon; -import brooklyn.entity.effector.Effectors; -import brooklyn.event.basic.PortAttributeSensorAndConfigKey; - -import org.apache.brooklyn.location.basic.PortRanges; - -/** - * PostgreSQL database node entity. - * <p> - * <ul> - * <li>You may need to increase shared memory settings in the kernel depending on the setting of - * the {@link #SHARED_MEMORY_BUFFER} key. The minimumm value is <em>128kB</em>. See the PostgreSQL - * <a href="http://www.postgresql.org/docs/9.1/static/kernel-resources.html">documentation</a>. - * <li>You will also need to enable passwordless sudo. - * </ul> - */ -@Catalog(name="PostgreSQL Node", description="PostgreSQL is an object-relational database management system (ORDBMS)", iconUrl="classpath:///postgresql-logo-200px.png") -@ImplementedBy(PostgreSqlNodeImpl.class) -public interface PostgreSqlNode extends SoftwareProcess, HasShortName, DatastoreCommon, DatabaseNode { - - @SetFromFlag("version") - ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "9.3-1");//"9.1-4"); - - @SetFromFlag("configFileUrl") - ConfigKey<String> CONFIGURATION_FILE_URL = ConfigKeys.newStringConfigKey( - "postgresql.config.file.url", "URL where PostgreSQL configuration file can be found; " - + "if not supplied the blueprint uses the default and customises it"); - - @SetFromFlag("authConfigFileUrl") - ConfigKey<String> AUTHENTICATION_CONFIGURATION_FILE_URL = ConfigKeys.newStringConfigKey( - "postgresql.authConfig.file.url", "URL where PostgreSQL host-based authentication configuration file can be found; " - + "if not supplied the blueprint uses the default and customises it"); - - @SetFromFlag("port") - PortAttributeSensorAndConfigKey POSTGRESQL_PORT = new PortAttributeSensorAndConfigKey( - "postgresql.port", "PostgreSQL port", PortRanges.fromString("5432+")); - - @SetFromFlag("sharedMemory") - ConfigKey<String> SHARED_MEMORY = ConfigKeys.newStringConfigKey( - "postgresql.sharedMemory", "Size of shared memory buffer (must specify as kB, MB or GB, minimum 128kB)", "4MB"); - - @SetFromFlag("maxConnections") - ConfigKey<Integer> MAX_CONNECTIONS = ConfigKeys.newIntegerConfigKey( - "postgresql.maxConnections", "Maximum number of connections to the database", 100); - - @SetFromFlag("disconnectOnStop") - ConfigKey<Boolean> DISCONNECT_ON_STOP = ConfigKeys.newBooleanConfigKey( - "postgresql.disconnect.on.stop", "If true, PostgreSQL will immediately disconnet (pg_ctl -m immediate stop) all current connections when the node is stopped", true); - - @SetFromFlag("pollPeriod") - ConfigKey<Long> POLL_PERIOD = ConfigKeys.newLongConfigKey( - "postgresql.sensorpoll", "Poll period (in milliseconds)", 1000L); - - Effector<String> EXECUTE_SCRIPT = Effectors.effector(DatastoreMixins.EXECUTE_SCRIPT) - .description("Executes the given script contents using psql") - .buildAbstract(); - - Integer getPostgreSqlPort(); - String getSharedMemory(); - Integer getMaxConnections(); - - String executeScript(String commands); - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeChefImplFromScratch.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeChefImplFromScratch.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeChefImplFromScratch.java deleted file mode 100644 index d82e637..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeChefImplFromScratch.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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.database.postgresql; - -import org.apache.brooklyn.api.entity.Effector; -import org.apache.brooklyn.core.util.ResourceUtils; -import org.apache.brooklyn.core.util.config.ConfigBag; -import org.apache.brooklyn.core.util.task.DynamicTasks; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.config.ConfigKey; -import brooklyn.entity.basic.ConfigKeys; -import brooklyn.entity.basic.EffectorStartableImpl; -import brooklyn.entity.basic.Entities; -import brooklyn.entity.chef.ChefConfig; -import brooklyn.entity.chef.ChefLifecycleEffectorTasks; -import brooklyn.entity.chef.ChefServerTasks; -import brooklyn.entity.effector.EffectorBody; -import brooklyn.entity.effector.Effectors; -import brooklyn.entity.software.SshEffectorTasks; -import brooklyn.event.feed.ssh.SshFeed; -import brooklyn.event.feed.ssh.SshPollConfig; - -import org.apache.brooklyn.location.basic.Locations; -import org.apache.brooklyn.location.basic.SshMachineLocation; - -import brooklyn.util.collections.Jsonya; -import brooklyn.util.guava.Maybe; -import brooklyn.util.ssh.BashCommands; - -public class PostgreSqlNodeChefImplFromScratch extends EffectorStartableImpl implements PostgreSqlNode { - - private static final Logger LOG = LoggerFactory.getLogger(PostgreSqlNodeChefImplFromScratch.class); - - public static final Effector<String> EXECUTE_SCRIPT = Effectors.effector(String.class, "executeScript") - .description("invokes a script") - .parameter(ExecuteScriptEffectorBody.SCRIPT) - .impl(new ExecuteScriptEffectorBody()).build(); - - private SshFeed feed; - - public void init() { - super.init(); - new ChefPostgreSqlLifecycle().attachLifecycleEffectors(this); - } - - @Override - public Integer getPostgreSqlPort() { return getAttribute(POSTGRESQL_PORT); } - - @Override - public String getSharedMemory() { return getConfig(SHARED_MEMORY); } - - @Override - public Integer getMaxConnections() { return getConfig(MAX_CONNECTIONS); } - - @Override - public String getShortName() { - return "PostgreSQL"; - } - - public static class ChefPostgreSqlLifecycle extends ChefLifecycleEffectorTasks { - { - usePidFile("/var/run/postgresql/*.pid"); - useService("postgresql"); - } - protected void startWithKnifeAsync() { - Entities.warnOnIgnoringConfig(entity(), ChefConfig.CHEF_LAUNCH_RUN_LIST); - Entities.warnOnIgnoringConfig(entity(), ChefConfig.CHEF_LAUNCH_ATTRIBUTES); - - DynamicTasks.queue( - ChefServerTasks - .knifeConvergeRunList("postgresql::server") - .knifeAddAttributes(Jsonya - .at("postgresql", "config").add( - "port", entity().getPostgreSqlPort(), - "listen_addresses", "*").getRootMap()) - .knifeAddAttributes(Jsonya - .at("postgresql", "pg_hba").list().map().add( - "type", "host", "db", "all", "user", "all", - "addr", "0.0.0.0/0", "method", "md5").getRootMap()) - // no other arguments currenty supported; chef will pick a password for us - ); - } - protected void postStartCustom() { - super.postStartCustom(); - - // now run the creation script - String creationScript; - String creationScriptUrl = entity().getConfig(PostgreSqlNode.CREATION_SCRIPT_URL); - if (creationScriptUrl != null) { - creationScript = ResourceUtils.create(entity()).getResourceAsString(creationScriptUrl); - } else { - creationScript = entity().getConfig(PostgreSqlNode.CREATION_SCRIPT_CONTENTS); - } - entity().executeScript(creationScript); - - // and finally connect sensors - entity().connectSensors(); - } - protected void preStopCustom() { - entity().disconnectSensors(); - super.preStopCustom(); - } - protected PostgreSqlNodeChefImplFromScratch entity() { - return (PostgreSqlNodeChefImplFromScratch) super.entity(); - } - } - - public static class ExecuteScriptEffectorBody extends EffectorBody<String> { - public static final ConfigKey<String> SCRIPT = ConfigKeys.newStringConfigKey("script", "contents of script to run"); - - public String call(ConfigBag parameters) { - return DynamicTasks.queue(SshEffectorTasks.ssh( - BashCommands.pipeTextTo( - parameters.get(SCRIPT), - BashCommands.sudoAsUser("postgres", "psql --file -"))) - .requiringExitCodeZero()).getStdout(); - } - } - - protected void connectSensors() { - setAttribute(DATASTORE_URL, String.format("postgresql://%s:%s/", getAttribute(HOSTNAME), getAttribute(POSTGRESQL_PORT))); - - Maybe<SshMachineLocation> machine = Locations.findUniqueSshMachineLocation(getLocations()); - - if (machine.isPresent()) { - feed = SshFeed.builder() - .entity(this) - .machine(machine.get()) - .poll(new SshPollConfig<Boolean>(SERVICE_UP) - .command("ps -ef | grep [p]ostgres") - .setOnSuccess(true) - .setOnFailureOrException(false)) - .build(); - } else { - LOG.warn("Location(s) {} not an ssh-machine location, so not polling for status; setting serviceUp immediately", getLocations()); - } - } - - protected void disconnectSensors() { - if (feed != null) feed.stop(); - } - - @Override - public String executeScript(String commands) { - return Entities.invokeEffector(this, this, EXECUTE_SCRIPT, - ConfigBag.newInstance().configure(ExecuteScriptEffectorBody.SCRIPT, commands).getAllConfig()).getUnchecked(); - } - - @Override - public void populateServiceNotUpDiagnostics() { - // TODO no-op currently; should check ssh'able etc - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeImpl.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeImpl.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeImpl.java deleted file mode 100644 index c4b02de..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeImpl.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.database.postgresql; - -import org.apache.brooklyn.core.util.config.ConfigBag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.entity.basic.SoftwareProcessImpl; -import brooklyn.entity.effector.EffectorBody; - -public class PostgreSqlNodeImpl extends SoftwareProcessImpl implements PostgreSqlNode { - - private static final Logger LOG = LoggerFactory.getLogger(PostgreSqlNodeImpl.class); - - public Class<?> getDriverInterface() { - return PostgreSqlDriver.class; - } - @Override - public PostgreSqlDriver getDriver() { - return (PostgreSqlDriver) super.getDriver(); - } - - @Override - public Integer getPostgreSqlPort() { return getAttribute(POSTGRESQL_PORT); } - - @Override - public String getSharedMemory() { return getConfig(SHARED_MEMORY); } - - @Override - public Integer getMaxConnections() { return getConfig(MAX_CONNECTIONS); } - - @Override - public void init() { - super.init(); - getMutableEntityType().addEffector(EXECUTE_SCRIPT, new EffectorBody<String>() { - @Override - public String call(ConfigBag parameters) { - return executeScript((String) parameters.getStringKey("commands")); - } - }); - } - - @Override - protected void connectSensors() { - super.connectSensors(); - connectServiceUpIsRunning(); - setAttribute(DATASTORE_URL, String.format("postgresql://%s:%s/", getAttribute(HOSTNAME), getAttribute(POSTGRESQL_PORT))); - } - - @Override - protected void disconnectSensors() { - disconnectServiceUpIsRunning(); - super.disconnectSensors(); - } - - @Override - public String getShortName() { - return "PostgreSQL"; - } - - @Override - public String executeScript(String commands) { - return getDriver() - .executeScriptAsync(commands) - .block() - .getStdout(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac1a7c09/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlSpecs.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlSpecs.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlSpecs.java deleted file mode 100644 index 5d90bdd..0000000 --- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlSpecs.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.database.postgresql; - -import org.apache.brooklyn.api.entity.proxying.EntitySpec; - -import brooklyn.entity.chef.ChefConfig; -import brooklyn.entity.chef.ChefConfig.ChefModes; - -/** - * Utiltiy for creating specs for {@link PostgreSqlNode} instances. - */ -public class PostgreSqlSpecs { - - private PostgreSqlSpecs() {} - - public static EntitySpec<PostgreSqlNode> spec() { - return EntitySpec.create(PostgreSqlNode.class); - } - - /** Requires {@code knife}. */ - public static EntitySpec<PostgreSqlNode> specChef() { - EntitySpec<PostgreSqlNode> spec = EntitySpec.create(PostgreSqlNode.class, PostgreSqlNodeChefImplFromScratch.class); - spec.configure(ChefConfig.CHEF_MODE, ChefModes.KNIFE); - return spec; - } -}
