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.
      *