You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/10/21 11:12:32 UTC
[1/3] incubator-brooklyn git commit: Adds SetHostnameCustomizer
Repository: incubator-brooklyn
Updated Branches:
refs/heads/master 238816f88 -> 9790e8b6b
Adds SetHostnameCustomizer
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/f792fdfb
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/f792fdfb
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/f792fdfb
Branch: refs/heads/master
Commit: f792fdfb3a4bccbf863997f401212657619317ef
Parents: 76b098b
Author: Aled Sage <al...@gmail.com>
Authored: Mon Oct 19 18:13:44 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Oct 21 09:50:30 2015 +0100
----------------------------------------------------------------------
.../core/effector/ssh/SshEffectorTasks.java | 7 +
.../entity/machine/SetHostnameCustomizer.java | 201 +++++++++++++++++++
.../machine/SetHostnameCustomizerLiveTest.java | 143 +++++++++++++
.../machine/SetHostnameCustomizerTest.java | 67 +++++++
4 files changed, 418 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f792fdfb/core/src/main/java/org/apache/brooklyn/core/effector/ssh/SshEffectorTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/ssh/SshEffectorTasks.java b/core/src/main/java/org/apache/brooklyn/core/effector/ssh/SshEffectorTasks.java
index 37e582d..a8e427c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/ssh/SshEffectorTasks.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/ssh/SshEffectorTasks.java
@@ -200,6 +200,13 @@ public class SshEffectorTasks {
}
}
+ /**
+ * @since 0.9.0
+ */
+ public static SshEffectorTaskFactory<Integer> ssh(SshMachineLocation machine, String ...commands) {
+ return new SshEffectorTaskFactory<Integer>(machine, commands);
+ }
+
public static SshEffectorTaskFactory<Integer> ssh(String ...commands) {
return new SshEffectorTaskFactory<Integer>(commands);
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f792fdfb/software/base/src/main/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizer.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizer.java b/software/base/src/main/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizer.java
new file mode 100644
index 0000000..7fdb86a
--- /dev/null
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizer.java
@@ -0,0 +1,201 @@
+/*
+ * 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 org.apache.brooklyn.entity.machine;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import org.apache.brooklyn.api.location.BasicMachineLocationCustomizer;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.SshEffectorTaskFactory;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.util.core.text.TemplateProcessor;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.ssh.BashCommands;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+/**
+ * Sets the hostname on an ssh'able machine. Currently only CentOS and RHEL are supported.
+ * <p>
+ * The customizer can be configured with a hard-coded hostname, or with a freemarker template
+ * whose value (after substitutions) will be used for the hostname.
+ */
+public class SetHostnameCustomizer extends BasicMachineLocationCustomizer {
+
+ public static final Logger log = LoggerFactory.getLogger(SetHostnameCustomizer.class);
+
+ public static final ConfigKey<String> FIXED_HOSTNAME = ConfigKeys.newStringConfigKey(
+ "hostname.fixed",
+ "The statically defined hostname to be set on the machine (if non-null)");
+
+ public static final ConfigKey<String> FIXED_DOMAIN = ConfigKeys.newStringConfigKey(
+ "domain.fixed",
+ "The statically defined domain name to be set on the machine (if non-null)");
+
+ // the var?? tests if it exists, passing value to ?string(if_present,if_absent)
+ // the ! provides a default value afterwards, which is never used, but is required for parsing
+ // when the config key is not available;
+ // thus the below prefers the first private address, then the first public address, and then
+ // substitutes dots for dashes.
+ public static final ConfigKey<String> HOSTNAME_TEMPLATE = ConfigKeys.newStringConfigKey(
+ "hostname.templated",
+ "The hostname template, to be resolved and then set on the machine (if non-null). "
+ +"Assumed to be in free-marker format.",
+ "ip-"
+ + "${((location.privateAddresses[0]??)?string("
+ + "location.privateAddresses[0]!'X', location.publicAddresses[0]))"
+ + "?replace(\".\",\"-\")}"
+ + "-${location.id}");
+
+ public static final ConfigKey<String> LOCAL_HOSTNAME = ConfigKeys.newStringConfigKey(
+ "hostname.local.hostname",
+ "Host name, as known on the local box. Config is set on the location.");
+
+ public static final ConfigKey<String> LOCAL_IP = ConfigKeys.newStringConfigKey(
+ "hostname.local.address",
+ "Host address, as known on the local box. Config is set on the location.");
+
+ private final ConfigBag config;
+
+ public SetHostnameCustomizer(ConfigBag config) {
+ // TODO Any checks that they've given us sufficient configuration?
+ this.config = config;
+ }
+
+ @Override
+ public void customize(MachineLocation machine) {
+ String localHostname = setLocalHostname((SshMachineLocation) machine);
+ machine.config().set(LOCAL_HOSTNAME, localHostname);
+
+ String localIp = execHostnameMinusI((SshMachineLocation) machine);
+ machine.config().set(LOCAL_IP, localIp);
+ }
+
+ protected String generateHostname(SshMachineLocation machine) {
+ String hostnameTemplate = config.get(HOSTNAME_TEMPLATE);
+ if (Strings.isNonBlank(hostnameTemplate)) {
+ return TemplateProcessor.processTemplateContents(hostnameTemplate, machine, ImmutableMap.<String, Object>of());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the machine's hostname to the value controlled by fixed_hostname and hostname_template.
+ * If these are blank (and fixed_domain is blank), then just return the current hostname of
+ * the machine.
+ */
+ public String setLocalHostname(SshMachineLocation machine) {
+ String hostFixed = config.get(FIXED_HOSTNAME);
+ String domainFixed = config.get(FIXED_DOMAIN);
+ String hostnameTemplate = config.get(HOSTNAME_TEMPLATE);
+
+ String hostname;
+ if (Strings.isNonBlank(hostFixed)) {
+ hostname = hostFixed;
+ } else {
+ if (Strings.isNonBlank(hostnameTemplate)) {
+ hostname = generateHostname(machine);
+ } else {
+ hostname = execHostname(machine);
+ if (Strings.isBlank(domainFixed)) {
+ return hostname;
+ }
+ }
+ }
+
+ return setLocalHostname(machine, hostname, domainFixed);
+ }
+
+ /**
+ * Sets the machine's hostname to the given value, ensuring /etc/hosts and /etc/sysconfig/network are both
+ * correctly updated.
+ */
+ public String setLocalHostname(SshMachineLocation machine, String hostName, String domainFixed) {
+ log.info("Setting local hostname of " + machine + " to " + hostName
+ + (Strings.isNonBlank(domainFixed) ? ", " + domainFixed : ""));
+
+ boolean hasDomain = Strings.isNonBlank(domainFixed);
+ String fqdn = hasDomain ? hostName+"."+domainFixed : hostName;
+
+ exec(machine, true,
+ BashCommands.sudo(String.format("sed -i.bak -e '1i127.0.0.1 %s %s' -e '/^127.0.0.1/d' /etc/hosts", fqdn, hostName)),
+ BashCommands.sudo(String.format("sed -i.bak -e 's/^HOSTNAME=.*$/HOSTNAME=%s/' /etc/sysconfig/network", fqdn)),
+ BashCommands.sudo(String.format("hostname %s", fqdn)));
+
+ return hostName;
+ }
+
+ protected void registerEtcHosts(SshMachineLocation machine, String ip, Iterable<String> hostnames) {
+ log.info("Updating /etc/hosts of "+machine+": adding "+ip+" = "+hostnames);
+
+ checkArgument(Strings.isNonBlank(ip) && Networking.isValidIp4(ip), "invalid IPv4 address %s", ip);
+ if (Strings.isBlank(ip) || Iterables.isEmpty(hostnames)) return;
+ String line = ip+" "+Joiner.on(" ").join(hostnames);
+ exec(machine, true, "echo " + line + " >> /etc/hosts");
+ }
+
+ protected String execHostname(SshMachineLocation machine) {
+ if (log.isDebugEnabled()) log.debug("Retrieve `hostname` via ssh for {}", machine);
+
+ ProcessTaskWrapper<Integer> cmd = exec(machine, false, "echo hostname=`hostname`");
+// ProcessTaskWrapper<Integer> cmd = DynamicTasks.queue(SshEffectorTasks.ssh(machine, "echo hostname=`hostname`")
+// .summary("getHostname"))
+// .block();
+
+ for (String line : cmd.getStdout().split("\n")) {
+ if (line.contains("hostname=") && !line.contains("`hostname`")) {
+ return line.substring(line.indexOf("hostname=") + "hostname=".length()).trim();
+ }
+ }
+ log.info("No hostname found for {} (got {}; {})", new Object[] {machine, cmd.getStdout(), cmd.getStderr()});
+ return null;
+ }
+
+ protected String execHostnameMinusI(SshMachineLocation machine) {
+ if (log.isDebugEnabled()) log.debug("Retrieve `hostname -I` via ssh for {}", machine);
+
+ ProcessTaskWrapper<Integer> cmd = exec(machine, false, "echo localip=`hostname -I`");
+
+ for (String line : cmd.getStdout().split("\n")) {
+ if (line.contains("localip=") && !line.contains("`hostname -I`")) {
+ return line.substring(line.indexOf("localip=") + "localip=".length()).trim();
+ }
+ }
+ log.info("No local ip found for {} (got {}; {})", new Object[] {machine, cmd.getStdout(), cmd.getStderr()});
+ return null;
+ }
+
+ protected ProcessTaskWrapper<Integer> exec(SshMachineLocation machine, boolean asRoot, String... cmds) {
+ SshEffectorTaskFactory<Integer> taskFactory = SshEffectorTasks.ssh(machine, cmds);
+ if (asRoot) taskFactory.runAsRoot();
+ return DynamicTasks.queue(taskFactory).block();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f792fdfb/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizerLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizerLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizerLiveTest.java
new file mode 100644
index 0000000..7e139f3
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizerLiveTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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 org.apache.brooklyn.entity.machine;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.location.Machines;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
+import org.apache.brooklyn.entity.AbstractSoftlayerLiveTest;
+import org.apache.brooklyn.location.jclouds.JcloudsLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class SetHostnameCustomizerLiveTest extends BrooklynAppLiveTestSupport {
+
+ public static final String PROVIDER = AbstractSoftlayerLiveTest.PROVIDER;
+ public static final String REGION = "ams01";
+ public static final String PROVIDER_IMAGE_ID = "CENTOS_6_64";
+ public static final String LOCATION_SPEC = PROVIDER + ":" + REGION;
+
+ public static final int MAX_TAG_LENGTH = AbstractSoftlayerLiveTest.MAX_TAG_LENGTH;
+ public static final int MAX_VM_NAME_LENGTH = AbstractSoftlayerLiveTest.MAX_VM_NAME_LENGTH;
+
+ protected BrooklynProperties brooklynProperties;
+
+ protected MachineProvisioningLocation<SshMachineLocation> loc;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ super.setUp();
+ List<String> propsToRemove = ImmutableList.of("imageId", "imageDescriptionRegex", "imageNameRegex", "inboundPorts", "hardwareId", "minRam");
+
+ // Don't let any defaults from brooklyn.properties (except credentials) interfere with test
+ brooklynProperties = BrooklynProperties.Factory.newDefault();
+ for (String propToRemove : propsToRemove) {
+ for (String propVariant : ImmutableList.of(propToRemove, CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propToRemove))) {
+ brooklynProperties.remove("brooklyn.locations.jclouds."+PROVIDER+"."+propVariant);
+ brooklynProperties.remove("brooklyn.locations."+propVariant);
+ brooklynProperties.remove("brooklyn.jclouds."+PROVIDER+"."+propVariant);
+ brooklynProperties.remove("brooklyn.jclouds."+propVariant);
+ }
+ }
+
+ // Also removes scriptHeader (e.g. if doing `. ~/.bashrc` and `. ~/.profile`, then that can cause "stdin: is not a tty")
+ brooklynProperties.remove("brooklyn.ssh.config.scriptHeader");
+
+ mgmt = new LocalManagementContext(brooklynProperties);
+
+ super.setUp();
+
+ loc = (MachineProvisioningLocation<SshMachineLocation>) mgmt.getLocationRegistry().resolve(LOCATION_SPEC);
+ }
+
+ @Test(groups = {"Live"})
+ public void testSetFixedHostname() throws Exception {
+ SetHostnameCustomizer customizer = new SetHostnameCustomizer(ConfigBag.newInstance()
+ .configure(SetHostnameCustomizer.FIXED_HOSTNAME, "myhostname"));
+
+ MachineEntity entity = app.createAndManageChild(EntitySpec.create(MachineEntity.class)
+ .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true)
+ .configure(MachineEntity.PROVISIONING_PROPERTIES.subKey(JcloudsLocation.MACHINE_LOCATION_CUSTOMIZERS.getName()), ImmutableSet.of(customizer))
+ .configure(MachineEntity.PROVISIONING_PROPERTIES.subKey(JcloudsLocation.IMAGE_ID.getName()), PROVIDER_IMAGE_ID));
+
+
+ app.start(ImmutableList.of(loc));
+
+ SshMachineLocation machine = Machines.findUniqueMachineLocation(entity.getLocations(), SshMachineLocation.class).get();
+
+ assertEquals(getHostname(machine), "myhostname");
+ }
+
+ @Test(groups = {"Live"})
+ public void testSetAutogeneratedHostname() throws Exception {
+ SetHostnameCustomizer customizer = new SetHostnameCustomizer(ConfigBag.newInstance());
+
+ MachineEntity entity = app.createAndManageChild(EntitySpec.create(MachineEntity.class)
+ .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true)
+ .configure(MachineEntity.PROVISIONING_PROPERTIES.subKey(JcloudsLocation.MACHINE_LOCATION_CUSTOMIZERS.getName()), ImmutableSet.of(customizer))
+ .configure(MachineEntity.PROVISIONING_PROPERTIES.subKey(JcloudsLocation.IMAGE_ID.getName()), "CENTOS_6_64"));
+
+
+ app.start(ImmutableList.of(loc));
+
+ SshMachineLocation machine = Machines.findUniqueMachineLocation(entity.getLocations(), SshMachineLocation.class).get();
+
+ String ip;
+ if (machine.getPrivateAddresses().isEmpty()) {
+ ip = Iterables.get(machine.getPublicAddresses(), 0);
+ } else {
+ ip = Iterables.get(machine.getPrivateAddresses(), 0);
+ }
+ String expected = "ip-"+(ip.replace(".", "-")+"-"+machine.getId());
+ assertEquals(getHostname(machine), expected);
+ }
+
+ protected String getHostname(SshMachineLocation machine) {
+ ByteArrayOutputStream outstream = new ByteArrayOutputStream();
+ ByteArrayOutputStream errstream = new ByteArrayOutputStream();
+ int result = machine.execScript(ImmutableMap.of("out", outstream, "err", errstream), "getHostname", ImmutableList.of("echo hostname=`hostname`"));
+ assertEquals(result, 0);
+
+ String out = new String(outstream.toByteArray());
+ String err = new String(errstream.toByteArray());
+ for (String line : out.split("\n")) {
+ if (line.contains("hostname=") && !line.contains("`hostname`")) {
+ return line.substring(line.indexOf("hostname=") + "hostname=".length()).trim();
+ }
+ }
+ throw new IllegalStateException(String.format("No hostname found for %s (got %s; %s)", machine, out, err));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f792fdfb/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizerTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizerTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizerTest.java
new file mode 100644
index 0000000..5ffdcdc
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/machine/SetHostnameCustomizerTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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 org.apache.brooklyn.entity.machine;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class SetHostnameCustomizerTest extends BrooklynAppUnitTestSupport {
+
+ private SetHostnameCustomizer customizer;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ super.setUp();
+ customizer = new SetHostnameCustomizer(ConfigBag.newInstance());
+ }
+
+ @Test
+ public void testGeneratedHostnameUsesPrivateIp() throws Exception {
+ SshMachineLocation machine = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .configure("privateAddresses", ImmutableList.of("1.2.3.4", "5.6.7.8"))
+ .configure("address", "4.3.2.1"));
+
+ assertEquals(customizer.generateHostname(machine), "ip-1-2-3-4-"+machine.getId());
+ }
+
+ @Test
+ public void testGeneratedHostnameUsesPublicIpIfNoPrivate() throws Exception {
+ SshMachineLocation machine = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .configure("address", "4.3.2.1"));
+
+ assertEquals(customizer.generateHostname(machine), "ip-4-3-2-1-"+machine.getId());
+ }
+
+ @Test
+ public void testGeneratedHostnameUsesPublicIpIfEmptyListOfPrivate() throws Exception {
+ SshMachineLocation machine = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .configure("privateAddresses", ImmutableList.of())
+ .configure("address", "4.3.2.1"));
+
+ assertEquals(customizer.generateHostname(machine), "ip-4-3-2-1-"+machine.getId());
+ }
+}
[3/3] incubator-brooklyn git commit: This closes #975
Posted by al...@apache.org.
This closes #975
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/9790e8b6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/9790e8b6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/9790e8b6
Branch: refs/heads/master
Commit: 9790e8b6b643a3cd4ebf2a98a1b4b89cd4bfd284
Parents: 238816f f792fdf
Author: Aled Sage <al...@gmail.com>
Authored: Wed Oct 21 10:12:20 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Oct 21 10:12:20 2015 +0100
----------------------------------------------------------------------
.../core/effector/ssh/SshEffectorTasks.java | 7 +
.../util/core/text/TemplateProcessor.java | 161 ++++++++++++++-
.../util/core/text/TemplateProcessorTest.java | 18 ++
.../entity/machine/SetHostnameCustomizer.java | 201 +++++++++++++++++++
.../machine/SetHostnameCustomizerLiveTest.java | 143 +++++++++++++
.../machine/SetHostnameCustomizerTest.java | 67 +++++++
6 files changed, 586 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
[2/3] incubator-brooklyn git commit: TemplateProcessor: for location
Posted by al...@apache.org.
TemplateProcessor: for location
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/76b098bd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/76b098bd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/76b098bd
Branch: refs/heads/master
Commit: 76b098bd1f01732647fb5f85ac2603cd0afb5c03
Parents: c2494dc
Author: Aled Sage <al...@gmail.com>
Authored: Tue Oct 20 14:59:58 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Oct 21 09:50:30 2015 +0100
----------------------------------------------------------------------
.../util/core/text/TemplateProcessor.java | 161 +++++++++++++++++--
.../util/core/text/TemplateProcessorTest.java | 18 +++
2 files changed, 168 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/76b098bd/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java b/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java
index 58efcd4..923f733 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/text/TemplateProcessor.java
@@ -18,6 +18,8 @@
*/
package org.apache.brooklyn.util.core.text;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@@ -33,6 +35,7 @@ import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.location.internal.LocationInternal;
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
import org.apache.brooklyn.core.sensor.DependentConfiguration;
import org.apache.brooklyn.core.sensor.Sensors;
@@ -115,6 +118,11 @@ public class TemplateProcessor {
return processTemplateContents(templateContents, new EntityAndMapTemplateModel(managementContext, extraSubstitutions));
}
+ /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
+ public static String processTemplateContents(String templateContents, Location location, Map<String,? extends Object> extraSubstitutions) {
+ return processTemplateContents(templateContents, new LocationAndMapTemplateModel((LocationInternal)location, extraSubstitutions));
+ }
+
/**
* A Freemarker {@link TemplateHashModel} which will correctly handle entries of the form "a.b" in this map,
* matching against template requests for "${a.b}".
@@ -181,7 +189,7 @@ public class TemplateProcessor {
/** FreeMarker {@link TemplateHashModel} which resolves keys inside the given entity or management context.
* Callers are required to include dots for dot-separated keys.
- * Freemarker will only due this when in inside bracket notation in an outer map, as in <code>${outer['a.b.']}</code>;
+ * Freemarker will only do this when in inside bracket notation in an outer map, as in <code>${outer['a.b.']}</code>;
* as a result this is intended only for use by {@link EntityAndMapTemplateModel} where
* a caller has used bracked notation, as in <code>${mgmt['key.subkey']}</code>. */
protected static final class EntityConfigTemplateModel implements TemplateHashModel {
@@ -189,13 +197,87 @@ public class TemplateProcessor {
protected final ManagementContext mgmt;
protected EntityConfigTemplateModel(EntityInternal entity) {
- this.entity = entity;
+ this.entity = checkNotNull(entity, "entity");
this.mgmt = entity.getManagementContext();
}
- protected EntityConfigTemplateModel(ManagementContext mgmt) {
- this.entity = null;
- this.mgmt = mgmt;
+ @Override
+ public boolean isEmpty() { return false; }
+
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ try {
+ Object result = entity.getConfig(ConfigKeys.builder(Object.class).name(key).build());
+
+ if (result==null)
+ result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
+
+ if (result!=null)
+ return wrapAsTemplateModel( result );
+
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ throw new IllegalStateException("Error accessing config '"+key+"'"+" on "+entity+": "+e, e);
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName()+"["+entity+"]";
+ }
+ }
+
+ /** FreeMarker {@link TemplateHashModel} which resolves keys inside the given management context.
+ * Callers are required to include dots for dot-separated keys.
+ * Freemarker will only do this when in inside bracket notation in an outer map, as in <code>${outer['a.b.']}</code>;
+ * as a result this is intended only for use by {@link EntityAndMapTemplateModel} where
+ * a caller has used bracked notation, as in <code>${mgmt['key.subkey']}</code>. */
+ protected static final class MgmtConfigTemplateModel implements TemplateHashModel {
+ protected final ManagementContext mgmt;
+
+ protected MgmtConfigTemplateModel(ManagementContext mgmt) {
+ this.mgmt = checkNotNull(mgmt, "mgmt");
+ }
+
+ @Override
+ public boolean isEmpty() { return false; }
+
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ try {
+ Object result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
+
+ if (result!=null)
+ return wrapAsTemplateModel( result );
+
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ throw new IllegalStateException("Error accessing config '"+key+"': "+e, e);
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName()+"["+mgmt+"]";
+ }
+ }
+
+ /** FreeMarker {@link TemplateHashModel} which resolves keys inside the given location.
+ * Callers are required to include dots for dot-separated keys.
+ * Freemarker will only do this when in inside bracket notation in an outer map, as in <code>${outer['a.b.']}</code>;
+ * as a result this is intended only for use by {@link LocationAndMapTemplateModel} where
+ * a caller has used bracked notation, as in <code>${mgmt['key.subkey']}</code>. */
+ protected static final class LocationConfigTemplateModel implements TemplateHashModel {
+ protected final LocationInternal location;
+ protected final ManagementContext mgmt;
+
+ protected LocationConfigTemplateModel(LocationInternal location) {
+ this.location = checkNotNull(location, "location");
+ this.mgmt = location.getManagementContext();
}
@Override
@@ -206,8 +288,8 @@ public class TemplateProcessor {
try {
Object result = null;
- if (entity!=null)
- result = entity.getConfig(ConfigKeys.builder(Object.class).name(key).build());
+ result = location.getConfig(ConfigKeys.builder(Object.class).name(key).build());
+
if (result==null && mgmt!=null)
result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
@@ -217,7 +299,7 @@ public class TemplateProcessor {
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
throw new IllegalStateException("Error accessing config '"+key+"'"
- + (entity!=null ? " on "+entity : "")+": "+e, e);
+ + (location!=null ? " on "+location : "")+": "+e, e);
}
return null;
@@ -225,7 +307,7 @@ public class TemplateProcessor {
@Override
public String toString() {
- return getClass().getName()+"["+entity+"]";
+ return getClass().getName()+"["+location+"]";
}
}
@@ -311,10 +393,10 @@ public class TemplateProcessor {
if (entity!=null)
return new EntityConfigTemplateModel(entity);
else
- return new EntityConfigTemplateModel(mgmt);
+ return new MgmtConfigTemplateModel(mgmt);
}
if ("mgmt".equals(key)) {
- return new EntityConfigTemplateModel(mgmt);
+ return new MgmtConfigTemplateModel(mgmt);
}
if ("driver".equals(key) && driver!=null)
@@ -351,6 +433,63 @@ public class TemplateProcessor {
}
}
+ /**
+ * Provides access to config on an entity or management context, using
+ * <code>${config['entity.config.key']}</code> or <code>${mgmt['brooklyn.properties.key']}</code> notation,
+ * and also allowing access to <code>getX()</code> methods on entity (interface) or driver
+ * using <code>${entity.x}</code> or <code><${driver.x}</code>.
+ * Optional extra properties can be supplied, treated as per {@link DotSplittingTemplateModel}.
+ */
+ protected static final class LocationAndMapTemplateModel implements TemplateHashModel {
+ protected final LocationInternal location;
+ protected final ManagementContext mgmt;
+ protected final DotSplittingTemplateModel extraSubstitutionsModel;
+
+ protected LocationAndMapTemplateModel(LocationInternal location, Map<String,? extends Object> extraSubstitutions) {
+ this.location = checkNotNull(location, "location");
+ this.mgmt = location.getManagementContext();
+ this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
+ }
+
+ @Override
+ public boolean isEmpty() { return false; }
+
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ if (extraSubstitutionsModel.contains(key))
+ return wrapAsTemplateModel( extraSubstitutionsModel.get(key) );
+
+ if ("location".equals(key))
+ return wrapAsTemplateModel( location );
+ if ("config".equals(key)) {
+ return new LocationConfigTemplateModel(location);
+ }
+ if ("mgmt".equals(key)) {
+ return new MgmtConfigTemplateModel(mgmt);
+ }
+
+ if (mgmt!=null) {
+ // TODO deprecated in 0.7.0, remove after next version
+ // ie not supported to access global props without qualification
+ Object result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
+ if (result!=null) {
+ log.warn("Deprecated access of global brooklyn.properties value for "+key+"; should be qualified with 'mgmt.'");
+ return wrapAsTemplateModel( result );
+ }
+ }
+
+ if ("javaSysProps".equals(key))
+ return wrapAsTemplateModel( System.getProperties() );
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName()+"["+location+"]";
+ }
+ }
+
/** Processes template contents with the given items in scope as per {@link EntityAndMapTemplateModel}. */
public static String processTemplateContents(String templateContents, final EntityInternal entity, Map<String,? extends Object> extraSubstitutions) {
return processTemplateContents(templateContents, new EntityAndMapTemplateModel(entity, extraSubstitutions));
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/76b098bd/core/src/test/java/org/apache/brooklyn/util/core/text/TemplateProcessorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/text/TemplateProcessorTest.java b/core/src/test/java/org/apache/brooklyn/util/core/text/TemplateProcessorTest.java
index b2fd6a7..0f2d989 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/text/TemplateProcessorTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/text/TemplateProcessorTest.java
@@ -26,6 +26,8 @@ import org.apache.brooklyn.core.sensor.DependentConfiguration;
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
import org.apache.brooklyn.core.test.entity.TestApplication;
import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.test.FixedLocaleTest;
import org.apache.brooklyn.util.core.text.TemplateProcessor;
import org.testng.Assert;
@@ -102,6 +104,22 @@ public class TemplateProcessorTest extends BrooklynAppUnitTestSupport {
}
@Test
+ public void testLocationGetterMethod() {
+ LocalhostMachineProvisioningLocation location = app.newLocalhostProvisioningLocation();
+ String templateContents = "${location.id}";
+ String result = TemplateProcessor.processTemplateContents(templateContents, location, ImmutableMap.<String,Object>of());
+ assertEquals(result, location.getId());
+ }
+
+ @Test
+ public void testLocationConfig() {
+ LocalhostMachineProvisioningLocation location = app.newLocalhostProvisioningLocation(ImmutableMap.of("mykey", "myval"));
+ String templateContents = "${config['mykey']}";//"+TestEntity.CONF_NAME.getName()+"']}";
+ String result = TemplateProcessor.processTemplateContents(templateContents, location, ImmutableMap.<String,Object>of());
+ assertEquals(result, "myval");
+ }
+
+ @Test
public void testManagementContextConfig() {
mgmt.getBrooklynProperties().put("globalmykey", "myval");
String templateContents = "${mgmt.globalmykey}";