blob: 1330f74022eadabfcd3011bb5105b80076e55452 [file] [log] [blame]
/*
* 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.jclouds.karaf.core;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.RunNodesException;
import org.jclouds.compute.RunScriptOnNodesException;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.ExecResponse;
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.domain.TemplateBuilder;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Location;
import org.jclouds.scriptbuilder.domain.Statement;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.util.concurrent.ListenableFuture;
/**
* A proxy implementations of the {@link ComputeService} which delegates calls to the underlying impl and notifies
* {@link NodeListener}s about node creation/destruction events.
*/
public class ComputeServiceEventProxy implements ComputeService {
private static final Logger LOGGER = LoggerFactory.getLogger(ComputeServiceEventProxy.class);
private BundleContext bundleContext;
private ComputeService computeService;
private ServiceTracker listenerTracker;
/**
* Constructor
*
* @param bundleContext
* @param computeService
*/
public ComputeServiceEventProxy(BundleContext bundleContext, ComputeService computeService) {
this.bundleContext = bundleContext;
this.computeService = computeService;
this.listenerTracker = new ServiceTracker(bundleContext, NodeListener.class.getName(), null);
this.listenerTracker.open();
}
private List<? extends NodeListener> getNodeListeners() {
List<NodeListener> listeners = new LinkedList<NodeListener>();
try {
listeners.addAll(Arrays.asList((NodeListener[]) listenerTracker.getServices()));
} catch (Exception ex) {
LOGGER.warn("Could not lookup node listeners. Listeners will not receive the last event.");
}
return listeners;
}
/**
* @return a reference to the context that created this ComputeService.
*/
@Override
public ComputeServiceContext getContext() {
return computeService.getContext();
}
/**
* Makes a new template builder for this service
*/
@Override
public TemplateBuilder templateBuilder() {
return computeService.templateBuilder();
}
/**
* Makes a new set of options for running nodes
*/
@Override
public TemplateOptions templateOptions() {
return computeService.templateOptions();
}
/**
* The list hardware profiles command shows you the options including virtual cpu count, memory,
* and disks. cpu count is not a portable quantity across clouds, as they are measured
* differently. However, it is a good indicator of relative speed within a cloud. memory is
* measured in megabytes and disks in gigabytes.
*
* @return a map of hardware profiles by ID, conceding that in some clouds the "id" is not used.
*/
@Override
public Set<? extends Hardware> listHardwareProfiles() {
return computeService.listHardwareProfiles();
}
/**
* Images define the operating system and metadata related to a node. In some clouds, Images are
* bound to a specific region, and their identifiers are different across these regions. For this
* reason, you should consider matching image requirements like operating system family with
* TemplateBuilder as opposed to choosing an image explicitly. The getImages() command returns a
* map of images by id.
*/
@Override
public Set<? extends Image> listImages() {
return computeService.listImages();
}
/**
* all nodes available to the current user by id. If possible, the returned set will include
* {@link org.jclouds.compute.domain.NodeMetadata} objects.
*/
@Override
public Set<? extends ComputeMetadata> listNodes() {
return computeService.listNodes();
}
/**
* all nodes available to the current user that have the given id. If possible, the returned set will include
* {@link org.jclouds.compute.domain.NodeMetadata} objects.
*/
@Override
public Set<? extends NodeMetadata> listNodesByIds(Iterable<String> ids) {
return computeService.listNodesByIds(ids);
}
/**
* The list locations command returns all the valid locations for nodes. A location has a scope,
* which is typically region or zone. A region is a general area, like eu-west, where a zone is
* similar to a datacenter. If a location has a parent, that implies it is within that location.
* For example a location can be a rack, whose parent is likely to be a zone.
*/
@Override
public Set<? extends Location> listAssignableLocations() {
return computeService.listAssignableLocations();
}
/**
* The compute api treats nodes as a group based on the name you specify. Using this group, you
* can choose to operate one or many nodes as a logical unit without regard to the implementation
* details of the cloud.
* <p/>
* <p/>
* The set that is returned will include credentials you can use to ssh into the nodes. The "key"
* part of the credentials is either a password or a private key. You have to inspect the value
* to determine this.
* <p/>
* <pre>
* if (node.getCredentials().key.startsWith("-----BEGIN RSA PRIVATE KEY-----"))
* // it is a private key, not a password.
* </pre>
* <p/>
* <p/>
* Note. if all you want to do is execute a script at bootup, you should consider use of the
* runscript option.
* <p/>
* If resources such as security groups are needed, they will be reused or created for you.
* Inbound port 22 will always be opened up.
*
* @param group - common identifier to group nodes by, cannot contain hyphens
* @param count - how many to fire up.
* @param template - how to configure the nodes
* @return all of the nodes the api was able to launch in a running state.
* @throws org.jclouds.compute.RunNodesException
* when there's a problem applying options to nodes. Note that successful and failed
* nodes are a part of this exception, so be sure to inspect this carefully.
*/
@Override
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, Template template) throws RunNodesException {
Set<? extends NodeMetadata> metadata = computeService.createNodesInGroup(group, count, template);
try {
for (NodeListener listener : getNodeListeners()) {
for (NodeMetadata node : metadata) {
listener.nodeCreated(node);
}
}
} catch (Exception ex) {
LOGGER.error("Error while notifying node listeners.", ex);
}
return metadata;
}
/**
* Like {@link org.jclouds.compute.ComputeService#createNodesInGroup(String, int, org.jclouds.compute.domain.Template)}, except that the template
* is default, equivalent to {@code templateBuilder().any().options(templateOptions)}.
*/
@Override
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, TemplateOptions templateOptions) throws RunNodesException {
Set<? extends NodeMetadata> metadata = computeService.createNodesInGroup(group, count, templateOptions);
try {
for (NodeListener listener : getNodeListeners()) {
for (NodeMetadata node : metadata) {
listener.nodeCreated(node);
}
}
} catch (Exception ex) {
LOGGER.error("Error while notifying node listeners.", ex);
}
return metadata;
}
/**
* Like {@link org.jclouds.compute.ComputeService#createNodesInGroup(String, int, org.jclouds.compute.options.TemplateOptions)}, except that the
* options are default, as specified in {@link org.jclouds.compute.ComputeService#templateOptions}.
*/
@Override
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count) throws RunNodesException {
Set<? extends NodeMetadata> metadata = computeService.createNodesInGroup(group, count);
try {
for (NodeListener listener : getNodeListeners()) {
for (NodeMetadata node : metadata) {
listener.nodeCreated(node);
}
}
} catch (Exception ex) {
LOGGER.error("Error while notifying node listeners.", ex);
}
return metadata;
}
/**
* resume the node from {@link org.jclouds.compute.domain.NodeState#SUSPENDED suspended} state,
* given its id.
* <p/>
* <h4>note</h4>
* <p/>
* affected nodes may not resume with the same IP address(es)
*/
@Override
public void resumeNode(String id) {
computeService.resumeNode(id);
}
/**
* nodes matching the filter are treated as a logical set. Using the resume command, you can save
* time by resuming the nodes in parallel.
* <p/>
* <h4>note</h4>
* <p/>
* affected nodes may not resume with the same IP address(es)
*
* @return list of nodes resumed
*
* @throws UnsupportedOperationException if the underlying provider doesn't support suspend/resume
* @throws java.util.NoSuchElementException
* if no nodes matched the predicate specified
*/
@Override
public Set<? extends NodeMetadata> resumeNodesMatching(Predicate<NodeMetadata> filter) {
return computeService.resumeNodesMatching(filter);
}
/**
* suspend the node, given its id. This will result in
* {@link org.jclouds.compute.domain.NodeState#SUSPENDED suspended} state.
* <p/>
* <h4>note</h4>
* <p/>
* affected nodes may not resume with the same IP address(es)
*
* @throws UnsupportedOperationException if the underlying provider doesn't support suspend/resume
*/
@Override
public void suspendNode(String id) {
computeService.suspendNode(id);
}
/**
* nodes matching the filter are treated as a logical set. Using the suspend command, you can
* save time by suspending the nodes in parallel.
* <p/>
* <h4>note</h4>
* <p/>
* affected nodes may not resume with the same IP address(es)
*
* @return list of nodes suspended
*
* @throws UnsupportedOperationException if the underlying provider doesn't support suspend/resume
* @throws java.util.NoSuchElementException
* if no nodes matched the predicate specified
*/
@Override
public Set<? extends NodeMetadata> suspendNodesMatching(Predicate<NodeMetadata> filter) {
return computeService.suspendNodesMatching(filter);
}
/**
* destroy the node, given its id. If it is the only node in a tag set, the dependent resources
* will also be destroyed.
*/
@Override
public void destroyNode(String id) {
NodeMetadata node = null;
try {
node = computeService.getNodeMetadata(id);
} catch (Exception ex) {
LOGGER.error("Error while retrieving node metadata.", ex);
}
computeService.destroyNode(id);
try {
for (NodeListener listener : getNodeListeners()) {
listener.nodeDestroyed(node);
}
} catch (Exception ex) {
LOGGER.error("Error while notifying node listeners.", ex);
}
}
/**
* nodes matching the filter are treated as a logical set. Using the delete command, you can save
* time by removing the nodes in parallel. When the last node in a set is destroyed, any indirect
* resources it uses, such as keypairs, are also destroyed.
*
* @return list of nodes destroyed
*/
@Override
public Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
Set<? extends NodeMetadata> metadata = computeService.destroyNodesMatching(filter);
try {
for (NodeListener listener : getNodeListeners()) {
for (NodeMetadata node : metadata) {
listener.nodeCreated(node);
}
}
} catch (Exception ex) {
LOGGER.error("Error while notifying node listeners.", ex);
}
return metadata;
}
/**
* reboot the node, given its id.
*/
@Override
public void rebootNode(String id) {
computeService.rebootNode(id);
}
/**
* nodes matching the filter are treated as a logical set. Using this command, you can save time
* by rebooting the nodes in parallel.
*
* @return list of nodes rebooted
*
* @throws java.util.NoSuchElementException
* if no nodes matched the predicate specified
*/
@Override
public Set<? extends NodeMetadata> rebootNodesMatching(Predicate<NodeMetadata> filter) {
return computeService.rebootNodesMatching(filter);
}
/**
* Find a node by its id.
*/
@Override
public NodeMetadata getNodeMetadata(String id) {
return computeService.getNodeMetadata(id);
}
/**
* get all nodes including details such as image and ip addresses even if it incurs extra
* requests to the service.
*
* @param filter how to select the nodes you are interested in details on.
*/
@Override
public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<ComputeMetadata> filter) {
return computeService.listNodesDetailsMatching(filter);
}
/**
* @see org.jclouds.compute.ComputeService#runScriptOnNodesMatching(com.google.common.base.Predicate, org.jclouds.scriptbuilder.domain.Statement, org.jclouds.compute.options.RunScriptOptions)
*/
@Override
public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, String runScript) throws RunScriptOnNodesException {
return computeService.runScriptOnNodesMatching(filter, runScript);
}
/**
* @see org.jclouds.compute.ComputeService#runScriptOnNodesMatching(com.google.common.base.Predicate, org.jclouds.scriptbuilder.domain.Statement, org.jclouds.compute.options.RunScriptOptions)
*/
@Override
public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, Statement runScript) throws RunScriptOnNodesException {
return computeService.runScriptOnNodesMatching(filter, runScript);
}
/**
* @see org.jclouds.compute.ComputeService#runScriptOnNodesMatching(com.google.common.base.Predicate, org.jclouds.scriptbuilder.domain.Statement, org.jclouds.compute.options.RunScriptOptions)
*/
@Override
public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, String runScript, RunScriptOptions options) throws RunScriptOnNodesException {
return computeService.runScriptOnNodesMatching(filter, runScript, options);
}
/**
* Run the script on all nodes with the specific predicate.
*
* @param filter Predicate-based filter to define on which nodes the script is to be executed
* @param runScript statement containing the script to run
* @param options nullable options to how to run the script, whether to override credentials
* @return map with node identifiers and corresponding responses
* @throws java.util.NoSuchElementException
* if no nodes matched the predicate specified
* @throws org.jclouds.compute.RunScriptOnNodesException
* if anything goes wrong during script execution
* @see org.jclouds.compute.predicates.NodePredicates#runningInGroup(String)
* @see org.jclouds.scriptbuilder.domain.Statements
*/
@Override
public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, Statement runScript, RunScriptOptions options) throws RunScriptOnNodesException {
return computeService.runScriptOnNodesMatching(filter, runScript, options);
}
/**
* Run the script on a specific node
*
* @param id node the script is to be executed on
* @param runScript statement containing the script to run
* @param options nullable options to how to run the script, whether to override credentials
* @return map with node identifiers and corresponding responses
* @throws java.util.NoSuchElementException
* if the node is not found
* @throws IllegalStateException if the node is not in running state
* @throws org.jclouds.compute.callables.ScriptStillRunningException
* if the script was still running after {@link org.jclouds.compute.reference.ComputeServiceConstants.Timeouts#scriptComplete}
* @see org.jclouds.compute.predicates.NodePredicates#runningInGroup(String)
* @see org.jclouds.scriptbuilder.domain.Statements
*/
@Override
public ExecResponse runScriptOnNode(String id, Statement runScript, RunScriptOptions options) {
return computeService.runScriptOnNode(id, runScript, options);
}
@Override
public ListenableFuture<ExecResponse> submitScriptOnNode(String s, String s2, RunScriptOptions runScriptOptions) {
return computeService.submitScriptOnNode(s, s2, runScriptOptions);
}
/**
* Run the script on a specific node in the background, typically as {@code nohup}
*
* @param id node the script is to be executed on
* @param runScript statement containing the script to run
* @param options nullable options to how to run the script, whether to override credentials
* @return map with node identifiers and corresponding responses
* @throws java.util.NoSuchElementException
* if the node is not found
* @throws IllegalStateException if the node is not in running state
* @see org.jclouds.compute.predicates.NodePredicates#runningInGroup(String)
* @see org.jclouds.scriptbuilder.domain.Statements
*/
@Override
@Beta
public ListenableFuture<ExecResponse> submitScriptOnNode(String id, Statement runScript, RunScriptOptions options) {
return computeService.submitScriptOnNode(id, runScript, options);
}
/**
* @see #runScriptOnNode(String, org.jclouds.scriptbuilder.domain.Statement, org.jclouds.compute.options.RunScriptOptions)
*/
@Override
public ExecResponse runScriptOnNode(String id, Statement runScript) {
return computeService.runScriptOnNode(id, runScript);
}
/**
* @see #runScriptOnNode(String, org.jclouds.scriptbuilder.domain.Statement, org.jclouds.compute.options.RunScriptOptions)
* @see org.jclouds.scriptbuilder.domain.Statements#exec
*/
@Override
public ExecResponse runScriptOnNode(String id, String runScript, RunScriptOptions options) {
return computeService.runScriptOnNode(id, runScript, options);
}
/**
* @see #runScriptOnNode(String, String, org.jclouds.compute.options.RunScriptOptions)
*/
@Override
public ExecResponse runScriptOnNode(String id, String runScript) {
return computeService.runScriptOnNode(id, runScript);
}
/**
* Find an image by its id.
* <p/>
* <h3>note</h3>
* <p/>
* This is an uncached call to the backend service
*/
@Override
public Image getImage(String id) {
return computeService.getImage(id);
}
/**
* Returns the {@link org.jclouds.compute.extensions.ImageExtension} for this provider if it implements it.
*
* @return an optional of the {@link org.jclouds.compute.extensions.ImageExtension} or {@link com.google.common.base.Optional#absent()} if not
* implemented
*/
@Override
public Optional<ImageExtension> getImageExtension() {
return computeService.getImageExtension();
}
/**
* Returns the {@link org.jclouds.compute.extensions.SecurityGroupExtension} for this provider if it implements it.
*
* @return an optional of the {@link org.jclouds.compute.extensions.SecurityGroupExtension} or {@link com.google.common.base.Optional#absent()} if not
* implemented
*/
@Override
public Optional<SecurityGroupExtension> getSecurityGroupExtension() {
return computeService.getSecurityGroupExtension();
}
}