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/07/02 18:22:47 UTC
[4/7] incubator-brooklyn git commit: ByonLocationResolver: support
complex machine config
ByonLocationResolver: support complex machine config
Adds support for each machine in BYON having more complex config,
such as each having its own set of config options (where each machine
has a map of config values).
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/4e625b9b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/4e625b9b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/4e625b9b
Branch: refs/heads/master
Commit: 4e625b9bf4ca6d1f847944b338be0032abf5646c
Parents: 64adbb7
Author: Aled Sage <al...@gmail.com>
Authored: Fri Jun 26 14:46:31 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jul 2 17:20:18 2015 +0100
----------------------------------------------------------------------
.../location/basic/ByonLocationResolver.java | 124 ++++++++++--
docs/guide/ops/locations/index.md | 25 +++
.../camp/brooklyn/ByonLocationsYamlTest.java | 193 +++++++++++++++++++
.../brooklyn/util/net/UserAndHostAndPort.java | 4 +
4 files changed, 326 insertions(+), 20 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e625b9b/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java b/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
index 2ce37d2..067af47 100644
--- a/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
+++ b/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
@@ -18,6 +18,8 @@
*/
package brooklyn.location.basic;
+import static com.google.common.base.Preconditions.checkArgument;
+
import java.net.InetAddress;
import java.util.List;
import java.util.Map;
@@ -27,18 +29,23 @@ import org.slf4j.LoggerFactory;
import brooklyn.config.ConfigKey;
import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Sanitizer;
import brooklyn.location.Location;
import brooklyn.location.LocationSpec;
import brooklyn.location.MachineLocation;
import brooklyn.management.internal.LocalLocationManager;
-import brooklyn.util.JavaGroovyEquivalents;
+import brooklyn.util.collections.MutableMap;
import brooklyn.util.config.ConfigBag;
+import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.net.UserAndHostAndPort;
import brooklyn.util.text.WildcardGlobs;
import brooklyn.util.text.WildcardGlobs.PhraseTreatment;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.net.HostAndPort;
/**
* Examples of valid specs:
@@ -86,7 +93,8 @@ public class ByonLocationResolver extends AbstractLocationResolver {
Object hosts = config.getStringKey("hosts");
config.remove("hosts");
- String user = (String)config.getStringKey("user");
+ String user = (String) config.getStringKey("user");
+ Integer port = (Integer) TypeCoercions.coerce(config.getStringKey("port"), Integer.class);
Class<? extends MachineLocation> locationClass = OS_TO_MACHINE_LOCATION_TYPE.get(config.get(OS_FAMILY));
List<String> hostAddresses;
@@ -108,25 +116,17 @@ public class ByonLocationResolver extends AbstractLocationResolver {
}
List<MachineLocation> machines = Lists.newArrayList();
- for (String host : hostAddresses) {
- String userHere = user;
- String hostHere = host;
- if (host.contains("@")) {
- userHere = host.substring(0, host.indexOf("@"));
- hostHere = host.substring(host.indexOf("@")+1);
- }
- try {
- InetAddress.getByName(hostHere.trim());
- } catch (Exception e) {
- throw new IllegalArgumentException("Invalid host '"+hostHere+"' specified in '"+spec+"': "+e);
- }
- LocationSpec<? extends MachineLocation> locationSpec = LocationSpec.create(locationClass)
- .configure("address", hostHere.trim())
- .configureIfNotNull(LocalLocationManager.CREATE_UNMANAGED, config.get(LocalLocationManager.CREATE_UNMANAGED));
- if (JavaGroovyEquivalents.groovyTruth(userHere)) {
- locationSpec.configure("user", userHere.trim());
+ for (Object host : hostAddresses) {
+ LocationSpec<? extends MachineLocation> machineSpec;
+ if (host instanceof String) {
+ machineSpec = parseMachine((String)host, locationClass, MutableMap.of("user", user, "port", port), spec);
+ } else if (host instanceof Map) {
+ machineSpec = parseMachine((Map<String, ?>)host, locationClass, MutableMap.of("user", user, "port", port), spec);
+ } else {
+ throw new IllegalArgumentException("Expected machine to be String or Map, but was "+host.getClass().getName()+" ("+host+")");
}
- MachineLocation machine = managementContext.getLocationManager().createLocation(locationSpec);
+ machineSpec.configureIfNotNull(LocalLocationManager.CREATE_UNMANAGED, config.get(LocalLocationManager.CREATE_UNMANAGED));
+ MachineLocation machine = managementContext.getLocationManager().createLocation(machineSpec);
machines.add(machine);
}
@@ -134,4 +134,88 @@ public class ByonLocationResolver extends AbstractLocationResolver {
return config;
}
+
+ protected LocationSpec<? extends MachineLocation> parseMachine(Map<String, ?> vals, Class<? extends MachineLocation> locationClass, Map<String, ?> defaults, String specForErrMsg) {
+ Map<String, Object> valSanitized = Sanitizer.sanitize(vals);
+ Map<String, Object> machineConfig = MutableMap.copyOf(vals);
+
+ String osfamily = (String) machineConfig.remove(OS_FAMILY.getName());
+ String ssh = (String) machineConfig.remove("ssh");
+ String winrm = (String) machineConfig.remove("winrm");
+ checkArgument(ssh != null ^ winrm != null, "Must specify exactly one of 'ssh' or 'winrm' for machine: %s", valSanitized);
+
+ UserAndHostAndPort userAndHostAndPort;
+ if (ssh != null) {
+ userAndHostAndPort = parseUserAndHostAndPort((String)ssh);
+ } else {
+ userAndHostAndPort = parseUserAndHostAndPort((String)winrm);
+ }
+
+ String host = userAndHostAndPort.getHostAndPort().getHostText().trim();
+ machineConfig.put("address", host);
+ try {
+ InetAddress.getByName(host);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Invalid host '"+host+"' specified in '"+specForErrMsg+"': "+e);
+ }
+
+ if (userAndHostAndPort.getUser() != null) {
+ checkArgument(!vals.containsKey("user"), "Must not specify user twice for machine: %s", valSanitized);
+ machineConfig.put("user", userAndHostAndPort.getUser());
+ }
+ if (userAndHostAndPort.getHostAndPort().hasPort()) {
+ checkArgument(!vals.containsKey("port"), "Must not specify port twice for machine: %s", valSanitized);
+ machineConfig.put("port", userAndHostAndPort.getHostAndPort().getPort());
+ }
+ for (Map.Entry<String, ?> entry : defaults.entrySet()) {
+ if (!machineConfig.containsKey(entry.getKey())) {
+ machineConfig.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ Class<? extends MachineLocation> locationClassHere = locationClass;
+ if (osfamily != null) {
+ locationClassHere = OS_TO_MACHINE_LOCATION_TYPE.get(osfamily);
+ }
+
+ return LocationSpec.create(locationClassHere).configure(machineConfig);
+ }
+
+ protected LocationSpec<? extends MachineLocation> parseMachine(String val, Class<? extends MachineLocation> locationClass, Map<String, ?> defaults, String specForErrMsg) {
+ Map<String, Object> machineConfig = Maps.newLinkedHashMap();
+
+ UserAndHostAndPort userAndHostAndPort = parseUserAndHostAndPort(val);
+
+ String host = userAndHostAndPort.getHostAndPort().getHostText().trim();
+ machineConfig.put("address", host);
+ try {
+ InetAddress.getByName(host.trim());
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Invalid host '"+host+"' specified in '"+specForErrMsg+"': "+e);
+ }
+
+ if (userAndHostAndPort.getUser() != null) {
+ machineConfig.put("user", userAndHostAndPort.getUser());
+ }
+ if (userAndHostAndPort.getHostAndPort().hasPort()) {
+ machineConfig.put("port", userAndHostAndPort.getHostAndPort().getPort());
+ }
+ for (Map.Entry<String, ?> entry : defaults.entrySet()) {
+ if (!machineConfig.containsKey(entry.getKey())) {
+ machineConfig.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ return LocationSpec.create(locationClass).configure(machineConfig);
+ }
+
+ private UserAndHostAndPort parseUserAndHostAndPort(String val) {
+ String userPart = null;
+ String hostPart = val;
+ if (val.contains("@")) {
+ userPart = val.substring(0, val.indexOf("@"));
+ hostPart = val.substring(val.indexOf("@")+1);
+ }
+ return UserAndHostAndPort.fromParts(userPart, HostAndPort.fromString(hostPart));
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e625b9b/docs/guide/ops/locations/index.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/locations/index.md b/docs/guide/ops/locations/index.md
index 6fc5b4b..28a238a 100644
--- a/docs/guide/ops/locations/index.md
+++ b/docs/guide/ops/locations/index.md
@@ -401,6 +401,31 @@ brooklyn.location.named.On-Prem\ Iron\ Example.privateKeyFile=~/.ssh/produser_id
brooklyn.location.named.On-Prem\ Iron\ Example.privateKeyPassphrase=s3cr3tpassphrase
{% endhighlight %}
+For more complex host configuration, one can define custom config values per machine. In the example
+below, there will be two machines. The first will be a machine reachable on
+`ssh -i ~/.ssh/brooklyn.pem -p 8022 myuser@50.51.52.53`. The second is a windows machine, reachable
+over WinRM. Each machine has also has a private address (e.g. for within a private network).
+
+{% highlight yaml %}
+location:
+ byon:
+ hosts:
+ - ssh: 50.51.52.53:8022
+ privateAddresses: [10.0.0.1]
+ privateKeyFile: ~/.ssh/brooklyn.pem
+ user: myuser
+ - winrm: 50.51.52.54:8985
+ privateAddresses: [10.0.0.2]
+ password: mypassword
+ user: myuser
+ osfamily: windows
+{% endhighlight %}
+
+The BYON location also supports a machine chooser, using the config key `byon.machineChooser`.
+This allows one to plugin logic to choose from the set of available machines in the pool. For
+example, additional config could be supplied for each machine. This could be used (during the call
+to `location.obtain()`) to find the config that matches the requirements of the entity being
+provisioned. See `brooklyn.location.basic.FixedListMachineProvisioningLocation.MACHINE_CHOOSER`.
### Other Location Topics
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e625b9b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
new file mode 100644
index 0000000..5ce0dac
--- /dev/null
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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 io.brooklyn.camp.brooklyn;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.StringReader;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.location.MachineLocation;
+import brooklyn.location.basic.FixedListMachineProvisioningLocation;
+import brooklyn.location.basic.LocationPredicates;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.location.basic.WinRmMachineLocation;
+import brooklyn.util.net.UserAndHostAndPort;
+
+import com.google.api.client.repackaged.com.google.common.base.Joiner;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class ByonLocationsYamlTest extends AbstractYamlTest {
+ private static final Logger log = LoggerFactory.getLogger(ByonLocationsYamlTest.class);
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testByonSpec() throws Exception {
+ String yaml = Joiner.on("\n").join(
+ "location: byon(user=myuser,mykey=myval,hosts=\"1.1.1.1\")",
+ "services:",
+ "- serviceType: brooklyn.entity.basic.BasicApplication");
+
+ Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+ FixedListMachineProvisioningLocation<SshMachineLocation> loc = (FixedListMachineProvisioningLocation<SshMachineLocation>) Iterables.get(app.getLocations(), 0);
+
+ Set<SshMachineLocation> machines = loc.getAvailable();
+ SshMachineLocation machine = Iterables.getOnlyElement(machines);
+ assertMachine(machine, UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 22), ImmutableMap.of("mykey", "myval"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testByonMachine() throws Exception {
+ String yaml = Joiner.on("\n").join(
+ "location:",
+ " byon:",
+ " hosts:",
+ " - ssh: 1.1.1.1:8022",
+ " privateAddresses: [10.0.0.1]",
+ " password: mypassword",
+ " user: myuser",
+ " mykey: myval",
+ "services:",
+ "- serviceType: brooklyn.entity.basic.BasicApplication");
+
+ Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+ FixedListMachineProvisioningLocation<SshMachineLocation> loc = (FixedListMachineProvisioningLocation<SshMachineLocation>) Iterables.get(app.getLocations(), 0);
+
+ Set<SshMachineLocation> machines = loc.getAvailable();
+ SshMachineLocation machine = Iterables.getOnlyElement(machines);
+ assertMachine(machine, UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 8022), ImmutableMap.of(
+ SshMachineLocation.PASSWORD.getName(), "mypassword",
+ "mykey", "myval"));
+ assertEquals(machine.getPrivateAddresses(), ImmutableSet.of("10.0.0.1"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testByonWindowsMachine() throws Exception {
+ String yaml = Joiner.on("\n").join(
+ "location:",
+ " byon:",
+ " hosts:",
+ " - winrm: 1.1.1.1:8985",
+ " privateAddresses: [10.0.0.1]",
+ " password: mypassword",
+ " user: myuser",
+ " mykey: myval",
+ " osfamily: windows",
+ "services:",
+ "- serviceType: brooklyn.entity.basic.BasicApplication");
+
+ Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+ FixedListMachineProvisioningLocation<WinRmMachineLocation> loc = (FixedListMachineProvisioningLocation<WinRmMachineLocation>) Iterables.get(app.getLocations(), 0);
+
+ Set<WinRmMachineLocation> machines = loc.getAvailable();
+ WinRmMachineLocation machine = Iterables.getOnlyElement(machines);
+ assertMachine(machine, UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 8985), ImmutableMap.of(
+ SshMachineLocation.PASSWORD.getName(), "mypassword",
+ "mykey", "myval"));
+ assertEquals(machine.getPrivateAddresses(), ImmutableSet.of("10.0.0.1"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testByonMultiMachine() throws Exception {
+ String yaml = Joiner.on("\n").join(
+ "location:",
+ " byon:",
+ " hosts:",
+ " - ssh: 1.1.1.1:8022",
+ " privateAddresses: [10.0.0.1]",
+ " password: mypassword",
+ " user: myuser",
+ " mykey: myval1",
+ " - ssh: 1.1.1.2:8022",
+ " privateAddresses: [10.0.0.2]",
+ " password: mypassword",
+ " user: myuser",
+ " mykey: myval2",
+ " - winrm: 1.1.1.3:8985",
+ " privateAddresses: [10.0.0.3]",
+ " password: mypassword",
+ " user: myuser",
+ " mykey: myval3",
+ " osfamily: windows",
+ "services:",
+ "- serviceType: brooklyn.entity.basic.BasicApplication");
+
+ Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+ FixedListMachineProvisioningLocation<MachineLocation> loc = (FixedListMachineProvisioningLocation<MachineLocation>) Iterables.get(app.getLocations(), 0);
+
+ Set<MachineLocation> machines = loc.getAvailable();
+ assertEquals(machines.size(), 3, "machines="+machines);
+ SshMachineLocation machine1 = (SshMachineLocation) Iterables.find(machines, LocationPredicates.configEqualTo(ConfigKeys.newStringConfigKey("mykey"), "myval1"));
+ SshMachineLocation machine2 = (SshMachineLocation) Iterables.find(machines, LocationPredicates.configEqualTo(ConfigKeys.newStringConfigKey("mykey"), "myval2"));
+ WinRmMachineLocation machine3 = (WinRmMachineLocation) Iterables.find(machines, Predicates.instanceOf(WinRmMachineLocation.class));
+
+ assertMachine(machine1, UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 8022), ImmutableMap.of(
+ SshMachineLocation.PASSWORD.getName(), "mypassword",
+ "mykey", "myval1"));
+ assertEquals(machine1.getPrivateAddresses(), ImmutableSet.of("10.0.0.1"));
+
+ assertMachine(machine2, UserAndHostAndPort.fromParts("myuser", "1.1.1.2", 8022), ImmutableMap.of(
+ SshMachineLocation.PASSWORD.getName(), "mypassword",
+ "mykey", "myval2"));
+ assertEquals(machine2.getPrivateAddresses(), ImmutableSet.of("10.0.0.2"));
+
+ assertMachine(machine3, UserAndHostAndPort.fromParts("myuser", "1.1.1.3", 8985), ImmutableMap.of(
+ SshMachineLocation.PASSWORD.getName(), "mypassword",
+ "mykey", "myval3"));
+ assertEquals(machine3.getPrivateAddresses(), ImmutableSet.of("10.0.0.3"));
+ }
+
+ private void assertMachine(SshMachineLocation machine, UserAndHostAndPort conn, Map<String, ?> config) {
+ assertEquals(machine.getAddress().getHostAddress(), conn.getHostAndPort().getHostText());
+ assertEquals(machine.getPort(), conn.getHostAndPort().getPort());
+ assertEquals(machine.getUser(), conn.getUser());
+ for (Map.Entry<String, ?> entry : config.entrySet()) {
+ Object actualVal = machine.getConfig(ConfigKeys.newConfigKey(Object.class, entry.getKey()));
+ assertEquals(actualVal, entry.getValue());
+ }
+ }
+
+ private void assertMachine(WinRmMachineLocation machine, UserAndHostAndPort conn, Map<String, ?> config) {
+ assertEquals(machine.getAddress().getHostAddress(), conn.getHostAndPort().getHostText());
+ assertEquals(machine.getConfig(WinRmMachineLocation.WINRM_PORT), (Integer) conn.getHostAndPort().getPort());
+ assertEquals(machine.getUser(), conn.getUser());
+ for (Map.Entry<String, ?> entry : config.entrySet()) {
+ Object actualVal = machine.getConfig(ConfigKeys.newConfigKey(Object.class, entry.getKey()));
+ assertEquals(actualVal, entry.getValue());
+ }
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return log;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e625b9b/utils/common/src/main/java/brooklyn/util/net/UserAndHostAndPort.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/net/UserAndHostAndPort.java b/utils/common/src/main/java/brooklyn/util/net/UserAndHostAndPort.java
index 49780b4..b2b67e7 100644
--- a/utils/common/src/main/java/brooklyn/util/net/UserAndHostAndPort.java
+++ b/utils/common/src/main/java/brooklyn/util/net/UserAndHostAndPort.java
@@ -31,6 +31,10 @@ public class UserAndHostAndPort implements Serializable {
return new UserAndHostAndPort(user, HostAndPort.fromParts(host, port));
}
+ public static UserAndHostAndPort fromParts(String user, HostAndPort hostAndPort) {
+ return new UserAndHostAndPort(user, hostAndPort);
+ }
+
/**
* Split a string of the form myuser@myhost:1234 into a user, host and port.
*