brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [07/23] brooklyn-server git commit: Add Karaf equivalents of cloud commands.
Date Fri, 13 May 2016 09:05:17 GMT
Add Karaf equivalents of cloud commands.

Refactors the CloudExplorer class into a core CloudExplorerSupport class
that does the work without knowing about any command line functionality,
plus a smaller CloudExplorer that does the command line handling and
invokes the CloudExplorerSupport to do the work.

Adds Karaf commands with the same names that use Karaf's command line
support to invoke the CloudExplorerSupport.

Adds the Karaf commands bundle as a boot feature so the commands are
readily available on startup.


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/bc244af9
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/bc244af9
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/bc244af9

Branch: refs/heads/master
Commit: bc244af9c7bc647ab46a26683a8978613cd3007d
Parents: 0408252
Author: Geoff Macartney <geoff.macartney@cloudsoftcorp.com>
Authored: Fri Apr 29 16:43:12 2016 +0100
Committer: Geoff Macartney <geoff.macartney@cloudsoftcorp.com>
Committed: Fri Apr 29 17:07:22 2016 +0100

----------------------------------------------------------------------
 karaf/apache-brooklyn/pom.xml                   |   2 +
 karaf/commands/pom.xml                          |   6 +
 .../apache/brooklyn/karaf/commands/Catalog.java |   2 -
 .../explorer/AbstractCloudExplorerCommand.java  |  53 +++
 .../commands/cloud/explorer/BlobCommand.java    |  45 ++
 .../cloud/explorer/DefaultTemplateCommand.java  |  38 ++
 .../cloud/explorer/GetImageCommand.java         |  44 ++
 .../cloud/explorer/ListContainerCommand.java    |  46 ++
 .../cloud/explorer/ListContainersCommand.java   |  38 ++
 .../explorer/ListHardwareProfilesCommand.java   |  38 ++
 .../cloud/explorer/ListImagesCommand.java       |  37 ++
 .../cloud/explorer/ListInstancesCommand.java    |  37 ++
 .../explorer/TerminateInstancesCommand.java     |  47 ++
 karaf/pom.xml                                   |   2 +-
 launcher-common/pom.xml                         |   5 +
 .../command/support/CloudExplorerSupport.java   | 437 +++++++++++++++++++
 .../org/apache/brooklyn/cli/CloudExplorer.java  | 327 ++++----------
 17 files changed, 954 insertions(+), 250 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/apache-brooklyn/pom.xml
----------------------------------------------------------------------
diff --git a/karaf/apache-brooklyn/pom.xml b/karaf/apache-brooklyn/pom.xml
index ee43acc..1851e3f 100755
--- a/karaf/apache-brooklyn/pom.xml
+++ b/karaf/apache-brooklyn/pom.xml
@@ -118,6 +118,8 @@
             <bootFeature>brooklyn-osgi-launcher</bootFeature>
             <bootFeature>brooklyn-jsgui</bootFeature>
             <bootFeature>brooklyn-rest-resources</bootFeature>
+            <bootFeature>brooklyn-commands</bootFeature>
+            <bootFeature>brooklyn-commands</bootFeature>
           </bootFeatures>
         </configuration>
       </plugin>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/pom.xml
----------------------------------------------------------------------
diff --git a/karaf/commands/pom.xml b/karaf/commands/pom.xml
index ed930b7..6fb3aa7 100644
--- a/karaf/commands/pom.xml
+++ b/karaf/commands/pom.xml
@@ -35,6 +35,12 @@
 
     <dependencies>
         <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-launcher-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.karaf.shell</groupId>
             <artifactId>org.apache.karaf.shell.core</artifactId>
             <version>${karaf.version}</version>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/Catalog.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/Catalog.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/Catalog.java
index 762672b..406df8f 100644
--- a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/Catalog.java
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/Catalog.java
@@ -18,14 +18,12 @@
  */
 package org.apache.brooklyn.karaf.commands;
 
-import com.google.common.annotations.Beta;
 import org.apache.karaf.shell.api.action.Action;
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 
-@Beta
 @Command(scope = "brooklyn", name = "catalog", description = "Manage the local brooklyn catalog")
 @Service
 public class Catalog implements Action {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/AbstractCloudExplorerCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/AbstractCloudExplorerCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/AbstractCloudExplorerCommand.java
new file mode 100644
index 0000000..c32041c
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/AbstractCloudExplorerCommand.java
@@ -0,0 +1,53 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+
+public abstract class AbstractCloudExplorerCommand implements Action {
+
+    public static final String CLOUD_EXPLORER_SCOPE = "cloud";
+
+    @Reference
+    private ManagementContext managementContext;
+
+    @Option(name = "--all-locations", description = "all locations")
+    protected boolean allLocations;
+
+    @Option(name = "--location", aliases = "-l", description = "a location specification")
+    protected String location;
+
+    @Option(name = "--auto-confirm", aliases = { "-y", "--yes" }, description = "automatically accept confirmation prompts")
+    protected boolean autoConfirm;
+
+    public void setManagementContext(ManagementContext managementContext) {
+        this.managementContext = managementContext;
+    }
+
+    public ManagementContext getManagementContext() {
+        return managementContext;
+    }
+
+    public void unsetManagementContext() {
+        this.managementContext = null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/BlobCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/BlobCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/BlobCommand.java
new file mode 100644
index 0000000..abbdf9b
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/BlobCommand.java
@@ -0,0 +1,45 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.GetBlob;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import static org.apache.brooklyn.karaf.commands.cloud.explorer.AbstractCloudExplorerCommand.CLOUD_EXPLORER_SCOPE;
+
+@Command(scope = CLOUD_EXPLORER_SCOPE, name = GetBlob.NAME, description = GetBlob.DESCRIPTION)
+@Service
+public class BlobCommand extends AbstractCloudExplorerCommand {
+
+    @Option(name = GetBlob.CONTAINER_ARGUMENT_NAME, description = GetBlob.CONTAINER_ARGUMENT_DESC, required = true)
+    private String container;
+
+    @Option(name = GetBlob.BLOB_ARGUMENT_NAME, description = GetBlob.BLOB_ARGUMENT_DESC, required = true)
+    private String blob;
+
+    @Override
+    public Object execute() throws Exception {
+
+        final GetBlob GetBlob =
+            new GetBlob(getManagementContext(), allLocations, location, autoConfirm, container, blob);
+        return GetBlob.call();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/DefaultTemplateCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/DefaultTemplateCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/DefaultTemplateCommand.java
new file mode 100644
index 0000000..ff282a2
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/DefaultTemplateCommand.java
@@ -0,0 +1,38 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.ComputeDefaultTemplate;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import static org.apache.brooklyn.karaf.commands.cloud.explorer.AbstractCloudExplorerCommand.CLOUD_EXPLORER_SCOPE;
+
+@Command(scope = CLOUD_EXPLORER_SCOPE, name = ComputeDefaultTemplate.NAME, description = ComputeDefaultTemplate.DESCRIPTION)
+@Service
+public class DefaultTemplateCommand extends AbstractCloudExplorerCommand {
+
+    @Override
+    public Object execute() throws Exception {
+
+        final ComputeDefaultTemplate ComputeDefaultTemplate =
+            new ComputeDefaultTemplate(getManagementContext(), allLocations, location, autoConfirm);
+        return ComputeDefaultTemplate.call();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/GetImageCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/GetImageCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/GetImageCommand.java
new file mode 100644
index 0000000..e128497
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/GetImageCommand.java
@@ -0,0 +1,44 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.GetImage;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.brooklyn.karaf.commands.cloud.explorer.AbstractCloudExplorerCommand.CLOUD_EXPLORER_SCOPE;
+
+@Command(scope = CLOUD_EXPLORER_SCOPE, name = GetImage.NAME, description = GetImage.DESCRIPTION)
+@Service
+public class GetImageCommand extends AbstractCloudExplorerCommand {
+
+    @Argument(name = GetImage.ARGUMENT_NAME, description = GetImage.ARGUMENT_DESC, required = true, multiValued = true)
+    private List<String> imageIds = new ArrayList<>();
+
+    @Override
+    public Object execute() throws Exception {
+
+        final GetImage getImage = new GetImage(getManagementContext(), allLocations, location, autoConfirm, imageIds);
+        return getImage.call();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListContainerCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListContainerCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListContainerCommand.java
new file mode 100644
index 0000000..2fea602
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListContainerCommand.java
@@ -0,0 +1,46 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.BlobstoreListContainer;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.brooklyn.karaf.commands.cloud.explorer.AbstractCloudExplorerCommand.CLOUD_EXPLORER_SCOPE;
+
+@Command(scope = CLOUD_EXPLORER_SCOPE, name = BlobstoreListContainer.NAME, description = BlobstoreListContainer.DESCRIPTION)
+@Service
+public class ListContainerCommand extends AbstractCloudExplorerCommand {
+
+    @Argument(name = BlobstoreListContainer.ARGUMENT_NAME, description = BlobstoreListContainer.ARGUMENT_DESC,
+        required = true, multiValued = true)
+    private List<String> imageIds = new ArrayList<>();
+
+    @Override
+    public Object execute() throws Exception {
+
+        final BlobstoreListContainer getImage = new BlobstoreListContainer(getManagementContext(), allLocations,
+            location, autoConfirm, imageIds);
+        return getImage.call();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListContainersCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListContainersCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListContainersCommand.java
new file mode 100644
index 0000000..40ea1b8
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListContainersCommand.java
@@ -0,0 +1,38 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.BlobstoreListContainers;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import static org.apache.brooklyn.karaf.commands.cloud.explorer.AbstractCloudExplorerCommand.CLOUD_EXPLORER_SCOPE;
+
+@Command(scope = CLOUD_EXPLORER_SCOPE, name = BlobstoreListContainers.NAME, description = BlobstoreListContainers.DESCRIPTION)
+@Service
+public class ListContainersCommand extends AbstractCloudExplorerCommand {
+
+    @Override
+    public Object execute() throws Exception {
+
+        final BlobstoreListContainers BlobstoreListContainers =
+            new BlobstoreListContainers(getManagementContext(), allLocations, location, autoConfirm);
+        return BlobstoreListContainers.call();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListHardwareProfilesCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListHardwareProfilesCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListHardwareProfilesCommand.java
new file mode 100644
index 0000000..e5bb006
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListHardwareProfilesCommand.java
@@ -0,0 +1,38 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.ListHardwareProfiles;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import static org.apache.brooklyn.karaf.commands.cloud.explorer.AbstractCloudExplorerCommand.CLOUD_EXPLORER_SCOPE;
+
+@Command(scope = CLOUD_EXPLORER_SCOPE, name = ListHardwareProfiles.NAME, description = ListHardwareProfiles.DESCRIPTION)
+@Service
+public class ListHardwareProfilesCommand extends AbstractCloudExplorerCommand {
+
+    @Override
+    public Object execute() throws Exception {
+
+        final ListHardwareProfiles profiles =
+            new ListHardwareProfiles(getManagementContext(), allLocations, location, autoConfirm);
+        return profiles.call();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListImagesCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListImagesCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListImagesCommand.java
new file mode 100644
index 0000000..3ee5003
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListImagesCommand.java
@@ -0,0 +1,37 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.ListImages;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import static org.apache.brooklyn.karaf.commands.cloud.explorer.AbstractCloudExplorerCommand.CLOUD_EXPLORER_SCOPE;
+
+@Command(scope = CLOUD_EXPLORER_SCOPE, name = ListImages.NAME, description = ListImages.DESCRIPTION)
+@Service
+public class ListImagesCommand extends AbstractCloudExplorerCommand {
+
+    @Override
+    public Object execute() throws Exception {
+
+        final ListImages ListImages = new ListImages(getManagementContext(), allLocations, location, autoConfirm);
+        return ListImages.call();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListInstancesCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListInstancesCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListInstancesCommand.java
new file mode 100644
index 0000000..8c63815
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/ListInstancesCommand.java
@@ -0,0 +1,37 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.ListInstances;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import static org.apache.brooklyn.karaf.commands.cloud.explorer.AbstractCloudExplorerCommand.CLOUD_EXPLORER_SCOPE;
+
+@Command(scope = CLOUD_EXPLORER_SCOPE, name = ListInstances.NAME, description = ListInstances.DESCRIPTION)
+@Service
+public class ListInstancesCommand extends AbstractCloudExplorerCommand {
+
+    @Override
+    public Object execute() throws Exception {
+
+        final ListInstances listInstances = new ListInstances(getManagementContext(), allLocations, location, autoConfirm);
+        return listInstances.call();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/TerminateInstancesCommand.java
----------------------------------------------------------------------
diff --git a/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/TerminateInstancesCommand.java b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/TerminateInstancesCommand.java
new file mode 100644
index 0000000..bdf00c0
--- /dev/null
+++ b/karaf/commands/src/main/java/org/apache/brooklyn/karaf/commands/cloud/explorer/TerminateInstancesCommand.java
@@ -0,0 +1,47 @@
+/*
+ * 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.karaf.commands.cloud.explorer;
+
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.TerminateInstances;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.brooklyn.karaf.commands.cloud.explorer.AbstractCloudExplorerCommand.CLOUD_EXPLORER_SCOPE;
+
+@Command(scope = CLOUD_EXPLORER_SCOPE, name = TerminateInstances.NAME, description = TerminateInstances.DESCRIPTION)
+@Service
+public class TerminateInstancesCommand extends AbstractCloudExplorerCommand {
+
+    @Argument(name = TerminateInstances.ARGUMENT_NAME, description = TerminateInstances.ARGUMENT_DESC, required = true,
+        multiValued = true)
+
+    private List<String> instanceIds = new ArrayList<>();
+
+    @Override
+    public Object execute() throws Exception {
+
+        final TerminateInstances TerminateInstances = new TerminateInstances(getManagementContext(), allLocations,
+            location, autoConfirm, instanceIds);
+        return TerminateInstances.call();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/karaf/pom.xml
----------------------------------------------------------------------
diff --git a/karaf/pom.xml b/karaf/pom.xml
index 816d9b0..256d868 100644
--- a/karaf/pom.xml
+++ b/karaf/pom.xml
@@ -56,8 +56,8 @@
     <module>init</module>
     <module>jetty-config</module>
     <module>features</module>
-    <module>apache-brooklyn</module>
     <module>commands</module>
+    <module>apache-brooklyn</module>
     <module>itest</module>
   </modules>
   

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/launcher-common/pom.xml
----------------------------------------------------------------------
diff --git a/launcher-common/pom.xml b/launcher-common/pom.xml
index 7b4821c..fe46c13 100644
--- a/launcher-common/pom.xml
+++ b/launcher-common/pom.xml
@@ -74,6 +74,11 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.brooklyn</groupId>
+            <artifactId>brooklyn-locations-jclouds</artifactId>
+            <version>0.10.0-SNAPSHOT</version>
+        </dependency>
     </dependencies>
 
    <build>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/launcher-common/src/main/java/org/apache/brooklyn/launcher/command/support/CloudExplorerSupport.java
----------------------------------------------------------------------
diff --git a/launcher-common/src/main/java/org/apache/brooklyn/launcher/command/support/CloudExplorerSupport.java b/launcher-common/src/main/java/org/apache/brooklyn/launcher/command/support/CloudExplorerSupport.java
new file mode 100644
index 0000000..690de92
--- /dev/null
+++ b/launcher-common/src/main/java/org/apache/brooklyn/launcher/command/support/CloudExplorerSupport.java
@@ -0,0 +1,437 @@
+/*
+ * 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.launcher.command.support;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.location.LocationConfigKeys;
+import org.apache.brooklyn.core.location.cloud.CloudLocationConfig;
+import org.apache.brooklyn.location.jclouds.JcloudsLocation;
+import org.apache.brooklyn.location.jclouds.JcloudsUtil;
+import org.apache.brooklyn.util.exceptions.FatalConfigurationRuntimeException;
+import org.apache.brooklyn.util.stream.Streams;
+import org.jclouds.blobstore.BlobStore;
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.domain.StorageMetadata;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.ComputeMetadata;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.options.TemplateOptions;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Convenience for listing Cloud Compute and BlobStore details.
+ * <p>
+ * For fuller functionality, consider instead the jclouds CLI or Ruby Fog CLI.
+ * <p>
+ * The advantage of this utility is that it piggie-backs off the {@code brooklyn.property} credentials,
+ * so requires less additional credential configuration. It also gives brooklyn-specific information,
+ * such as which image will be used by default in a given cloud.
+ */
+public abstract class CloudExplorerSupport implements Callable<Void> {
+
+    private ManagementContext managementContext;
+
+    public static final String ALL_LOCATIONS_DESC =
+        "All locations (i.e. all locations in brooklyn.properties for which there are credentials)";
+    public boolean allLocations;
+
+    public static final String LOCATION_DESC =
+        "A location spec (e.g. referring to a named location in brooklyn.properties file)";
+    public String location;
+
+    public static final String AUTOCONFIRM_DESC = "Automatically answer yes to any questions";
+    public boolean autoconfirm = false;
+
+
+    @VisibleForTesting
+    protected PrintStream stdout = System.out;
+
+    @VisibleForTesting
+    protected PrintStream stderr = System.err;
+
+    @VisibleForTesting
+    protected InputStream stdin = System.in;
+
+    public CloudExplorerSupport(ManagementContext managementContext, boolean allLocations, String location, boolean autoconfirm) {
+        this.managementContext = managementContext;
+        this.allLocations = allLocations;
+        this.location = location;
+        this.autoconfirm = autoconfirm;
+    }
+
+    protected abstract void doCall(JcloudsLocation loc, String indent) throws Exception;
+
+    @Override
+    public Void call() throws Exception {
+        List<JcloudsLocation> locs = Lists.newArrayList();
+        if (location != null && allLocations) {
+            throw new FatalConfigurationRuntimeException("Must not specify --location and --all-locations");
+        } else if (location != null) {
+            JcloudsLocation loc = (JcloudsLocation) managementContext.getLocationRegistry().getLocationManaged(location);
+            locs.add(loc);
+        } else if (allLocations) {
+            // Find all named locations that point at different target clouds
+            Map<String, LocationDefinition> definedLocations = managementContext.getLocationRegistry().getDefinedLocations();
+            for (LocationDefinition locationDef : definedLocations.values()) {
+
+                Location loc = managementContext.getLocationManager().createLocation(
+                    managementContext.getLocationRegistry().getLocationSpec(locationDef).get() );
+
+                if (loc instanceof JcloudsLocation) {
+                    boolean found = false;
+                    for (JcloudsLocation existing : locs) {
+                        if (equalTargets(existing, (JcloudsLocation) loc)) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found) {
+                        locs.add((JcloudsLocation) loc);
+                    }
+                }
+            }
+        } else {
+            throw new FatalConfigurationRuntimeException("Must specify one of --location or --all-locations");
+        }
+
+        for (JcloudsLocation loc : locs) {
+            stdout.println("Location {");
+            stdout.println("\tprovider: "+loc.getProvider());
+            stdout.println("\tdisplayName: "+loc.getDisplayName());
+            stdout.println("\tidentity: "+loc.getIdentity());
+            if (loc.getEndpoint() != null) stdout.println("\tendpoint: "+loc.getEndpoint());
+            if (loc.getRegion() != null) stdout.println("\tregion: "+loc.getRegion());
+
+            try {
+                doCall(loc, "\t");
+            } finally {
+                stdout.println("}");
+            }
+        }
+
+        return null;
+    }
+
+    protected boolean equalTargets(JcloudsLocation loc1, JcloudsLocation loc2) {
+        return Objects.equal(loc1.getProvider(), loc2.getProvider())
+            && Objects.equal(loc1.getIdentity(), loc2.getIdentity())
+            && Objects.equal(loc1.getEndpoint(), loc2.getEndpoint())
+            && Objects.equal(loc1.getRegion(), loc2.getRegion());
+    }
+
+
+    public static abstract class ComputeExploration extends CloudExplorerSupport {
+        protected abstract void doCall(ComputeService computeService, String indent) throws Exception;
+
+        public ComputeExploration(ManagementContext managementContext, boolean allLocations, String location,
+                                  boolean autoconfirm) {
+            super(managementContext, allLocations, location, autoconfirm);
+        }
+
+        @Override
+        protected void doCall(JcloudsLocation loc, String indent) throws Exception {
+            ComputeService computeService = loc.getComputeService();
+            doCall(computeService, indent);
+        }
+    }
+
+    public static class ListInstances extends ComputeExploration {
+        public static final String NAME = "list-instances";
+        public static final String DESCRIPTION = "list instances";
+
+        public ListInstances(ManagementContext managementContext, boolean allLocations, String location,
+                             boolean autoconfirm) {
+            super(managementContext, allLocations, location, autoconfirm);
+        }
+
+        @Override
+        protected void doCall(ComputeService computeService, String indent) throws Exception {
+            Set<? extends ComputeMetadata> instances = computeService.listNodes();
+            stdout.println(indent+"Instances {");
+            for (ComputeMetadata instance : instances) {
+                stdout.println(indent+"\t"+instance);
+            }
+            stdout.println(indent+"}");
+        }
+    }
+
+    public static class ListImages extends ComputeExploration {
+        public static final String NAME = "list-images";
+        public static final String DESCRIPTION = "list images";
+
+        public ListImages(ManagementContext managementContext, boolean allLocations, String location,
+                          boolean autoconfirm) {
+            super(managementContext, allLocations, location, autoconfirm);
+        }
+
+        @Override
+        protected void doCall(ComputeService computeService, String indent) throws Exception {
+            Set<? extends Image> images = computeService.listImages();
+            stdout.println(indent+"Images {");
+            for (Image image : images) {
+                stdout.println(indent+"\t"+image);
+            }
+            stdout.println(indent+"}");
+        }
+    }
+
+    public static class ListHardwareProfiles extends ComputeExploration {
+        public static final String NAME = "list-hardware-profiles";
+        public static final String DESCRIPTION = "list hardware profiles";
+
+        public ListHardwareProfiles(ManagementContext managementContext, boolean allLocations, String location,
+                                    boolean autoconfirm) {
+            super(managementContext, allLocations, location, autoconfirm);
+        }
+
+        @Override
+        protected void doCall(ComputeService computeService, String indent) throws Exception {
+            Set<? extends Hardware> hardware = computeService.listHardwareProfiles();
+            stdout.println(indent+"Hardware Profiles {");
+            for (Hardware image : hardware) {
+                stdout.println(indent+"\t"+image);
+            }
+            stdout.println(indent+"}");
+        }
+    }
+
+    public static class GetImage extends ComputeExploration {
+        public static final String NAME = "get-image";
+        public static final String DESCRIPTION = "get image details for one or more imageIds";
+        public static final String ARGUMENT_NAME = "imageIds";
+        public static final String ARGUMENT_DESC = "IDs of the images to retrieve";
+
+        private List<String> names;
+
+        public GetImage(ManagementContext managementContext, boolean allLocations, String location,
+                        boolean autoconfirm, List<String> arguments) {
+            super(managementContext, allLocations, location, autoconfirm);
+            names = arguments;
+        }
+
+        @Override
+        protected void doCall(ComputeService computeService, String indent) throws Exception {
+
+            for (String imageId : names) {
+                Image image = computeService.getImage(imageId);
+                if (image == null) {
+                    return;
+                }
+                stdout.println(indent+"Image "+imageId+" {");
+                stdout.println(indent+"\t"+image);
+                stdout.println(indent+"}");
+            }
+        }
+    }
+
+    public static class ComputeDefaultTemplate extends CloudExplorerSupport {
+        public static final String NAME = "default-template";
+        public static final String DESCRIPTION = "compute default template";
+
+        public ComputeDefaultTemplate(ManagementContext managementContext, boolean allLocations, String location,
+                                      boolean autoconfirm) {
+            super(managementContext, allLocations, location, autoconfirm);
+        }
+
+        @Override
+        protected void doCall(JcloudsLocation loc, String indent) throws Exception {
+
+            ComputeService computeService = loc.getComputeService();
+
+            Template template = loc.buildTemplate(computeService, loc.config().getBag());
+            Image image = template.getImage();
+            Hardware hardware = template.getHardware();
+            org.jclouds.domain.Location location = template.getLocation();
+            TemplateOptions options = template.getOptions();
+            stdout.println(indent+"Default template {");
+            stdout.println(indent+"\tImage: "+image);
+            stdout.println(indent+"\tHardware: "+hardware);
+            stdout.println(indent+"\tLocation: "+location);
+            stdout.println(indent+"\tOptions: "+options);
+            stdout.println(indent+"}");
+        }
+    }
+
+    public static class TerminateInstances extends ComputeExploration {
+        public static final String NAME = "terminate-instances";
+        public static final String DESCRIPTION = "terminate instances for one or more instance IDs";
+        public static final String ARGUMENT_NAME = "instanceIds";
+        public static final String ARGUMENT_DESC = "IDs of the instances to terminate";
+        private List<String> names;
+
+        public TerminateInstances(ManagementContext managementContext, boolean allLocations, String location,
+                                  boolean autoconfirm, List<String> names) {
+            super(managementContext, allLocations, location, autoconfirm);
+            this.names = names;
+        }
+
+        @Override
+        protected void doCall(ComputeService computeService, String indent) throws Exception {
+
+            for (String instanceId : names) {
+                NodeMetadata instance = computeService.getNodeMetadata(instanceId);
+                if (instance == null) {
+                    stderr.println(indent+"Cannot terminate instance; could not find "+instanceId);
+                } else {
+                    boolean confirmed = confirm(indent, "terminate "+instanceId+" ("+instance+")");
+                    if (confirmed) {
+                        computeService.destroyNode(instanceId);
+                    }
+                }
+            }
+        }
+    }
+
+    public static abstract class Blobstore extends CloudExplorerSupport {
+        public Blobstore(ManagementContext managementContext, boolean allLocations, String location,
+                         boolean autoconfirm) {
+            super(managementContext, allLocations, location, autoconfirm);
+        }
+
+        protected abstract void doCall(org.jclouds.blobstore.BlobStore blobstore, String indent) throws Exception;
+
+        @Override
+        protected void doCall(JcloudsLocation loc, String indent) throws Exception {
+            String identity = checkNotNull(loc.getConfig(LocationConfigKeys.ACCESS_IDENTITY), "identity must not be null");
+            String credential = checkNotNull(loc.getConfig(LocationConfigKeys.ACCESS_CREDENTIAL), "credential must not be null");
+            String provider = checkNotNull(loc.getConfig(LocationConfigKeys.CLOUD_PROVIDER), "provider must not be null");
+            String endpoint = loc.getConfig(CloudLocationConfig.CLOUD_ENDPOINT);
+
+            BlobStoreContext context = JcloudsUtil.newBlobstoreContext(provider, endpoint, identity, credential);
+            try {
+                org.jclouds.blobstore.BlobStore blobStore = context.getBlobStore();
+                doCall(blobStore, indent);
+            } finally {
+                context.close();
+            }
+        }
+    }
+
+    public static class BlobstoreListContainers extends Blobstore {
+        public static final String NAME = "list-containers";
+        public static final String DESCRIPTION = "list containers";
+
+        public BlobstoreListContainers(ManagementContext managementContext, boolean allLocations, String location,
+                                       boolean autoconfirm) {
+            super(managementContext, allLocations, location, autoconfirm);
+        }
+
+        @Override
+        protected void doCall(BlobStore blobstore, String indent) throws Exception {
+            Set<? extends StorageMetadata> containers = blobstore.list();
+            stdout.println(indent+"Containers {");
+            for (StorageMetadata container : containers) {
+                stdout.println(indent+"\t"+container);
+            }
+            stdout.println(indent+"}");
+        }
+    }
+
+    public static class BlobstoreListContainer extends Blobstore {
+        public static final String NAME = "list-container";
+        public static final String DESCRIPTION = "list container details for one or more container names";
+        public static final String ARGUMENT_NAME = "container-names";
+        public static final String ARGUMENT_DESC = "names of the containers to list";
+        private List<String> names;
+
+        public BlobstoreListContainer(ManagementContext managementContext, boolean allLocations, String location,
+                                      boolean autoconfirm, List<String> names) {
+            super(managementContext, allLocations, location, autoconfirm);
+            this.names = names;
+        }
+
+        @Override
+        protected void doCall(BlobStore blobStore, String indent) throws Exception {
+            for (String containerName : names) {
+                Set<? extends StorageMetadata> contents = blobStore.list(containerName);
+                stdout.println(indent+"Container "+containerName+" {");
+                for (StorageMetadata content : contents) {
+                    stdout.println(indent+"\t"+content);
+                }
+                stdout.println(indent+"}");
+            }
+        }
+    }
+
+    public static class GetBlob extends Blobstore {
+        public static final String NAME = "blob";
+        public static final String DESCRIPTION = "list blob details for a given container and blob";
+        public static final String CONTAINER_ARGUMENT_NAME = "--container";
+        public static final String CONTAINER_ARGUMENT_DESC = "name of the container of the blob";
+        public static final String BLOB_ARGUMENT_NAME = "--blob";
+        public static final String BLOB_ARGUMENT_DESC = "name of the blob in the container";
+        public String container;
+        public String blob;
+
+        public GetBlob(ManagementContext managementContext, boolean allLocations, String location, boolean autoconfirm,
+                       String container, String blob) {
+            super(managementContext, allLocations, location, autoconfirm);
+            this.container = container;
+            this.blob = blob;
+        }
+
+        @Override
+        protected void doCall(BlobStore blobStore, String indent) throws Exception {
+            Blob content = blobStore.getBlob(container, blob);
+            stdout.println(indent+"Blob "+container+" : " +blob +" {");
+            stdout.println(indent+"\tHeaders {");
+            for (Map.Entry<String, String> entry : content.getAllHeaders().entries()) {
+                stdout.println(indent+"\t\t"+entry.getKey() + " = " + entry.getValue());
+            }
+            stdout.println(indent+"\t}");
+            stdout.println(indent+"\tmetadata : "+content.getMetadata());
+            stdout.println(indent+"\tpayload : "+ Streams.readFullyString(content.getPayload().openStream()));
+            stdout.println(indent+"}");
+        }
+    }
+
+    protected boolean confirm(String msg, String indent) throws Exception {
+        if (autoconfirm) {
+            stdout.println(indent+"Auto-confirmed: "+msg);
+            return true;
+        } else {
+            stdout.println(indent+"Enter y/n. Are you sure you want to "+msg);
+            int in = stdin.read();
+            boolean confirmed = (Character.toLowerCase(in) == 'y');
+            if (confirmed) {
+                stdout.println(indent+"Confirmed; will "+msg);
+            } else {
+                stdout.println(indent+"Declined; will not "+msg);
+            }
+            return confirmed;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc244af9/server-cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java
----------------------------------------------------------------------
diff --git a/server-cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java b/server-cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java
index fa464f8..5018ae6 100644
--- a/server-cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java
+++ b/server-cli/src/main/java/org/apache/brooklyn/cli/CloudExplorer.java
@@ -18,358 +18,191 @@
  */
 package org.apache.brooklyn.cli;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.BlobstoreListContainer;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.BlobstoreListContainers;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.GetBlob;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.GetImage;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.ComputeDefaultTemplate;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.ListImages;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.ListInstances;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.ListHardwareProfiles;
+import org.apache.brooklyn.launcher.command.support.CloudExplorerSupport.TerminateInstances;
 import io.airlift.command.Command;
 import io.airlift.command.Option;
 import io.airlift.command.ParseException;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.jclouds.blobstore.BlobStore;
-import org.jclouds.blobstore.BlobStoreContext;
-import org.jclouds.blobstore.domain.Blob;
-import org.jclouds.blobstore.domain.StorageMetadata;
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.domain.ComputeMetadata;
-import org.jclouds.compute.domain.Hardware;
-import org.jclouds.compute.domain.Image;
-import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.options.TemplateOptions;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.LocationDefinition;
-import org.apache.brooklyn.core.location.LocationConfigKeys;
-import org.apache.brooklyn.core.location.cloud.CloudLocationConfig;
 import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
-import org.apache.brooklyn.location.jclouds.JcloudsLocation;
-import org.apache.brooklyn.location.jclouds.JcloudsUtil;
-import org.apache.brooklyn.util.exceptions.FatalConfigurationRuntimeException;
-import org.apache.brooklyn.util.stream.Streams;
 
-import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.collect.Lists;
 
 /**
- * Convenience for listing Cloud Compute and BlobStore details.
- * <p>
- * For fuller functionality, consider instead the jclouds CLI or Ruby Fog CLI.
- * <p>
- * The advantage of this utility is that it piggie-backs off the {@code brooklyn.property} credentials,
- * so requires less additional credential configuration. It also gives brooklyn-specific information,
- * such as which image will be used by default in a given cloud.
+ * Makes use of {@link CloudExplorerSupport} to provide cloud explorer commands at Brooklyn server command line.
  */
 public class CloudExplorer {
 
     public static abstract class JcloudsCommand extends AbstractMain.BrooklynCommandCollectingArgs {
         @Option(name = { "--all-locations" }, title = "all locations",
-                description = "All locations (i.e. all locations in brooklyn.properties for which there are credentials)")
+                description = CloudExplorerSupport.ALL_LOCATIONS_DESC)
         public boolean allLocations;
         
         @Option(name = { "-l", "--location" }, title = "location spec",
-                description = "A location spec (e.g. referring to a named location in brooklyn.properties file)")
+                description = CloudExplorerSupport.LOCATION_DESC)
         public String location;
 
         @Option(name = { "-y", "--yes" }, title = "auto-confirm",
-                description = "Automatically answer yes to any questions")
-        public boolean autoconfirm = false;
+                description = CloudExplorerSupport.AUTOCONFIRM_DESC)
+        public boolean autoConfirm = false;
+
+
+        protected abstract CloudExplorerSupport getExplorer(LocalManagementContext mgmt, boolean allLocations,
+                                                            String location, boolean autoconfirm);
 
-        protected abstract void doCall(JcloudsLocation loc, String indent) throws Exception;
-        
         @Override
         public Void call() throws Exception {
             LocalManagementContext mgmt = new LocalManagementContext();
-            List<JcloudsLocation> locs = Lists.newArrayList();
             try {
-                if (location != null && allLocations) {
-                    throw new FatalConfigurationRuntimeException("Must not specify --location and --all-locations");
-                } else if (location != null) {
-                    JcloudsLocation loc = (JcloudsLocation) mgmt.getLocationRegistry().getLocationManaged(location);
-                    locs.add(loc);
-                } else if (allLocations) {
-                    // Find all named locations that point at different target clouds
-                    Map<String, LocationDefinition> definedLocations = mgmt.getLocationRegistry().getDefinedLocations();
-                    for (LocationDefinition locationDef : definedLocations.values()) {
-                        Location loc = mgmt.getLocationManager().createLocation( mgmt.getLocationRegistry().getLocationSpec(locationDef).get() );
-                        if (loc instanceof JcloudsLocation) {
-                            boolean found = false;
-                            for (JcloudsLocation existing : locs) {
-                                if (equalTargets(existing, (JcloudsLocation) loc)) {
-                                    found = true;
-                                    break;
-                                }
-                            }
-                            if (!found) {
-                                locs.add((JcloudsLocation) loc);
-                            }
-                        }
-                    }
-                } else {
-                    throw new FatalConfigurationRuntimeException("Must specify one of --location or --all-locations");
-                }
-                
-                for (JcloudsLocation loc : locs) {
-                    stdout.println("Location {");
-                    stdout.println("\tprovider: "+loc.getProvider());
-                    stdout.println("\tdisplayName: "+loc.getDisplayName());
-                    stdout.println("\tidentity: "+loc.getIdentity());
-                    if (loc.getEndpoint() != null) stdout.println("\tendpoint: "+loc.getEndpoint());
-                    if (loc.getRegion() != null) stdout.println("\tregion: "+loc.getRegion());
-
-                    try {
-                        doCall(loc, "\t");
-                    } finally {
-                        stdout.println("}");
-                    }
-                }
+                CloudExplorerSupport explorer = getExplorer(mgmt, allLocations, location, autoConfirm);
+                explorer.call();
             } finally {
                 mgmt.terminate();
             }
             return null;
         }
-        
+
         @Override
         public ToStringHelper string() {
             return super.string()
                     .add("location", location);
         }
-        
-        protected boolean equalTargets(JcloudsLocation loc1, JcloudsLocation loc2) {
-            return Objects.equal(loc1.getProvider(), loc2.getProvider())
-                    && Objects.equal(loc1.getIdentity(), loc2.getIdentity())
-                    && Objects.equal(loc1.getEndpoint(), loc2.getEndpoint())
-                    && Objects.equal(loc1.getRegion(), loc2.getRegion());
-        }
-        
-        
-        protected boolean confirm(String msg, String indent) throws Exception {
-            if (autoconfirm) {
-                stdout.println(indent+"Auto-confirmed: "+msg);
-                return true;
-            } else {
-                stdout.println(indent+"Enter y/n. Are you sure you want to "+msg);
-                int in = stdin.read();
-                boolean confirmed = (Character.toLowerCase(in) == 'y');
-                if (confirmed) {
-                    stdout.println(indent+"Confirmed; will "+msg);
-                } else {
-                    stdout.println(indent+"Declined; will not "+msg);
-                }
-                return confirmed;
-            }
-        }
     }
     
-    public static abstract class ComputeCommand extends JcloudsCommand {
-        protected abstract void doCall(ComputeService computeService, String indent) throws Exception;
-            
-        @Override
-        protected void doCall(JcloudsLocation loc, String indent) throws Exception {
-            ComputeService computeService = loc.getComputeService();
-            doCall(computeService, indent);
-        }
-    }
 
-    @Command(name = "list-instances", description = "")
-    public static class ComputeListInstancesCommand extends ComputeCommand {
+    @Command(name = ListInstances.NAME, description = ListInstances.DESCRIPTION)
+    public static class ComputeListInstancesCommand extends JcloudsCommand {
         @Override
-        protected void doCall(ComputeService computeService, String indent) throws Exception {
+        protected CloudExplorerSupport
+        getExplorer(LocalManagementContext mgmt, boolean allLocations, String location, boolean autoconfirm) {
             failIfArguments();
-            Set<? extends ComputeMetadata> instances = computeService.listNodes();
-            stdout.println(indent+"Instances {");
-            for (ComputeMetadata instance : instances) {
-                stdout.println(indent+"\t"+instance);
-            }
-            stdout.println(indent+"}");
+            return new ListInstances(mgmt, allLocations, location, autoconfirm);
         }
     }
 
-    @Command(name = "list-images", description = "")
-    public static class ComputeListImagesCommand extends ComputeCommand {
+    @Command(name = ListImages.NAME, description = ListImages.DESCRIPTION)
+    public static class ComputeListImagesCommand extends JcloudsCommand {
         @Override
-        protected void doCall(ComputeService computeService, String indent) throws Exception {
+        protected CloudExplorerSupport
+        getExplorer(LocalManagementContext mgmt, boolean allLocations, String location, boolean autoconfirm) {
             failIfArguments();
-            Set<? extends Image> images = computeService.listImages();
-            stdout.println(indent+"Images {");
-            for (Image image : images) {
-                stdout.println(indent+"\t"+image);
-            }
-            stdout.println(indent+"}");
+            return new ListImages(mgmt, allLocations, location, autoconfirm);
         }
     }
     
-    @Command(name = "list-hardware-profiles", description = "")
-    public static class ComputeListHardwareProfilesCommand extends ComputeCommand {
+    @Command(name = ListHardwareProfiles.NAME, description = ListHardwareProfiles.DESCRIPTION)
+    public static class ComputeListHardwareProfilesCommand extends JcloudsCommand {
         @Override
-        protected void doCall(ComputeService computeService, String indent) throws Exception {
+        protected CloudExplorerSupport
+        getExplorer(LocalManagementContext mgmt, boolean allLocations, String location, boolean autoconfirm) {
             failIfArguments();
-            Set<? extends Hardware> hardware = computeService.listHardwareProfiles();
-            stdout.println(indent+"Hardware Profiles {");
-            for (Hardware image : hardware) {
-                stdout.println(indent+"\t"+image);
-            }
-            stdout.println(indent+"}");
+            return new CloudExplorerSupport.ListHardwareProfiles(mgmt, allLocations, location, autoconfirm);
         }
     }
     
-    @Command(name = "get-image", description = "")
-    public static class ComputeGetImageCommand extends ComputeCommand {
+    @Command(name = GetImage.NAME, description = GetImage.DESCRIPTION)
+    public static class ComputeGetImageCommand extends JcloudsCommand {
+
         @Override
-        protected void doCall(ComputeService computeService, String indent) throws Exception {
+        protected CloudExplorerSupport
+        getExplorer(LocalManagementContext mgmt, boolean allLocations, String location, boolean autoconfirm) {
             if (arguments.isEmpty()) {
                 throw new ParseException("Requires at least one image-id arguments");
             }
-            
-            for (String imageId : arguments) {
-                Image image = computeService.getImage(imageId);
-                stdout.println(indent+"Image "+imageId+" {");
-                stdout.println(indent+"\t"+image);
-                stdout.println(indent+"}");
-            }
+            return new GetImage(mgmt, allLocations, location, autoconfirm, arguments);
         }
-        
+
         @Override
         public ToStringHelper string() {
             return super.string()
-                    .add("imageIds", arguments);
+                    .add(GetImage.ARGUMENT_NAME, arguments);
         }
     }
 
-    @Command(name = "default-template", description = "")
+    @Command(name = ComputeDefaultTemplate.NAME, description = ComputeDefaultTemplate.DESCRIPTION)
     public static class ComputeDefaultTemplateCommand extends JcloudsCommand {
+
         @Override
-        protected void doCall(JcloudsLocation loc, String indent) throws Exception {
+        protected CloudExplorerSupport
+        getExplorer(LocalManagementContext mgmt, boolean allLocations, String location, boolean autoconfirm) {
             failIfArguments();
-            ComputeService computeService = loc.getComputeService();
-            
-            Template template = loc.buildTemplate(computeService, loc.config().getBag());
-            Image image = template.getImage();
-            Hardware hardware = template.getHardware();
-            org.jclouds.domain.Location location = template.getLocation();
-            TemplateOptions options = template.getOptions();
-            stdout.println(indent+"Default template {");
-            stdout.println(indent+"\tImage: "+image);
-            stdout.println(indent+"\tHardware: "+hardware);
-            stdout.println(indent+"\tLocation: "+location);
-            stdout.println(indent+"\tOptions: "+options);
-            stdout.println(indent+"}");
+            return new CloudExplorerSupport.ComputeDefaultTemplate(mgmt, allLocations, location, autoconfirm);
         }
     }
     
-    @Command(name = "terminate-instances", description = "")
-    public static class ComputeTerminateInstancesCommand extends ComputeCommand {
+    @Command(name = TerminateInstances.NAME, description = TerminateInstances.DESCRIPTION)
+    public static class ComputeTerminateInstancesCommand extends JcloudsCommand {
+
         @Override
-        protected void doCall(ComputeService computeService, String indent) throws Exception {
+        protected CloudExplorerSupport
+        getExplorer(LocalManagementContext mgmt, boolean allLocations, String location, boolean autoconfirm) {
             if (arguments.isEmpty()) {
                 throw new ParseException("Requires at least one instance-id arguments");
             }
-            
-            for (String instanceId : arguments) {
-                NodeMetadata instance = computeService.getNodeMetadata(instanceId);
-                if (instance == null) {
-                    stderr.println(indent+"Cannot terminate instance; could not find "+instanceId);
-                } else {
-                    boolean confirmed = confirm(indent, "terminate "+instanceId+" ("+instance+")");
-                    if (confirmed) {
-                        computeService.destroyNode(instanceId);
-                    }
-                }
-            }
+            return new CloudExplorerSupport.TerminateInstances(mgmt, allLocations, location, autoconfirm, arguments);
         }
-        
+
         @Override
         public ToStringHelper string() {
             return super.string()
-                    .add("instanceIds", arguments);
+                    .add(TerminateInstances.ARGUMENT_NAME, arguments);
         }
     }
 
-    public static abstract class BlobstoreCommand extends JcloudsCommand {
-        protected abstract void doCall(BlobStore blobstore, String indent) throws Exception;
+
+    @Command(name = BlobstoreListContainers.NAME, description = BlobstoreListContainers.DESCRIPTION)
+    public static class BlobstoreListContainersCommand extends JcloudsCommand {
 
         @Override
-        protected void doCall(JcloudsLocation loc, String indent) throws Exception {
-            String identity = checkNotNull(loc.getConfig(LocationConfigKeys.ACCESS_IDENTITY), "identity must not be null");
-            String credential = checkNotNull(loc.getConfig(LocationConfigKeys.ACCESS_CREDENTIAL), "credential must not be null");
-            String provider = checkNotNull(loc.getConfig(LocationConfigKeys.CLOUD_PROVIDER), "provider must not be null");
-            String endpoint = loc.getConfig(CloudLocationConfig.CLOUD_ENDPOINT);
-            
-            BlobStoreContext context = JcloudsUtil.newBlobstoreContext(provider, endpoint, identity, credential);
-            try {
-                BlobStore blobStore = context.getBlobStore();
-                doCall(blobStore, indent);
-            } finally {
-                context.close();
-            }
-        }
-    }
-    
-    @Command(name = "list-containers", description = "")
-    public static class BlobstoreListContainersCommand extends BlobstoreCommand {
-        @Override
-        protected void doCall(BlobStore blobstore, String indent) throws Exception {
+        protected CloudExplorerSupport
+        getExplorer(LocalManagementContext mgmt, boolean allLocations, String location, boolean autoconfirm) {
             failIfArguments();
-            Set<? extends StorageMetadata> containers = blobstore.list();
-            stdout.println(indent+"Containers {");
-            for (StorageMetadata container : containers) {
-                stdout.println(indent+"\t"+container);
-            }
-            stdout.println(indent+"}");
+            return new BlobstoreListContainers(mgmt, allLocations, location, autoconfirm);
         }
     }
 
-    @Command(name = "list-container", description = "")
-    public static class BlobstoreListContainerCommand extends BlobstoreCommand {
+    @Command(name = BlobstoreListContainer.NAME, description = BlobstoreListContainer.DESCRIPTION)
+    public static class BlobstoreListContainerCommand extends JcloudsCommand {
+
         @Override
-        protected void doCall(BlobStore blobStore, String indent) throws Exception {
+        protected CloudExplorerSupport
+        getExplorer(LocalManagementContext mgmt, boolean allLocations, String location, boolean autoconfirm) {
             if (arguments.isEmpty()) {
                 throw new ParseException("Requires at least one container-name arguments");
             }
-            
-            for (String containerName : arguments) {
-                Set<? extends StorageMetadata> contents = blobStore.list(containerName);
-                stdout.println(indent+"Container "+containerName+" {");
-                for (StorageMetadata content : contents) {
-                    stdout.println(indent+"\t"+content);
-                }
-                stdout.println(indent+"}");
-            }
+            return new BlobstoreListContainer(mgmt, allLocations, location, autoconfirm, arguments);
         }
-        
+
         @Override
         public ToStringHelper string() {
             return super.string()
-                    .add("containers", arguments);
+                    .add(BlobstoreListContainer.ARGUMENT_NAME, arguments);
         }
     }
     
-    @Command(name = "blob", description = "")
-    public static class BlobstoreGetBlobCommand extends BlobstoreCommand {
-        @Option(name = { "--container" }, title = "list contents of a given container",
-                description = "")
+    @Command(name = GetBlob.NAME, description = GetBlob.DESCRIPTION)
+    public static class BlobstoreGetBlobCommand extends JcloudsCommand {
+        @Option(name = { GetBlob.CONTAINER_ARGUMENT_NAME}, description = GetBlob.CONTAINER_ARGUMENT_DESC)
         public String container;
 
-        @Option(name = { "--blob" }, title = "retrieves the blog in the given container",
-                description = "")
+        @Option(name = { GetBlob.BLOB_ARGUMENT_NAME}, description = GetBlob.BLOB_ARGUMENT_DESC)
         public String blob;
 
         @Override
-        protected void doCall(BlobStore blobStore, String indent) throws Exception {
+        protected CloudExplorerSupport
+        getExplorer(LocalManagementContext mgmt, boolean allLocations, String location, boolean autoconfirm) {
             failIfArguments();
-            Blob content = blobStore.getBlob(container, blob);
-            stdout.println(indent+"Blob "+container+" : " +blob +" {");
-            stdout.println(indent+"\tHeaders {");
-            for (Map.Entry<String, String> entry : content.getAllHeaders().entries()) {
-                stdout.println(indent+"\t\t"+entry.getKey() + " = " + entry.getValue());
-            }
-            stdout.println(indent+"\t}");
-            stdout.println(indent+"\tmetadata : "+content.getMetadata());
-            stdout.println(indent+"\tpayload : "+Streams.readFullyString(content.getPayload().openStream()));
-            stdout.println(indent+"}");
+            return new GetBlob(mgmt, allLocations, location, autoconfirm, container, blob);
         }
-        
+
         @Override
         public ToStringHelper string() {
             return super.string()


Mime
View raw message