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 <[email protected]>
Authored: Fri Apr 29 16:43:12 2016 +0100
Committer: Geoff Macartney <[email protected]>
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()

Reply via email to