mirror of https://github.com/apache/jclouds.git
JCLOUDS-106. Add proper support for generation/detection of keypair/security groups to CloudStack.
This commit is contained in:
parent
7651657cfc
commit
fbe637c8bf
|
@ -15,6 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.cloudstack;
|
||||
import static org.jclouds.cloudstack.config.CloudStackProperties.AUTO_GENERATE_KEYPAIRS;
|
||||
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||
|
||||
import java.net.URI;
|
||||
|
@ -62,6 +63,7 @@ public class CloudStackApiMetadata extends BaseRestApiMetadata {
|
|||
Properties properties = BaseRestApiMetadata.defaultProperties();
|
||||
properties.setProperty("jclouds.ssh.max-retries", "7");
|
||||
properties.setProperty("jclouds.ssh.retry-auth", "true");
|
||||
properties.setProperty(AUTO_GENERATE_KEYPAIRS, "false");
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.cloudstack.compute;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
|
||||
import static org.jclouds.cloudstack.predicates.SshKeyPairPredicates.nameMatches;
|
||||
import static org.jclouds.cloudstack.predicates.ZonePredicates.supportsSecurityGroups;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.collect.Memoized;
|
||||
import org.jclouds.compute.ComputeServiceContext;
|
||||
import org.jclouds.compute.callables.RunScriptOnNode;
|
||||
import org.jclouds.compute.domain.Hardware;
|
||||
import org.jclouds.compute.domain.Image;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.TemplateBuilder;
|
||||
import org.jclouds.compute.extensions.ImageExtension;
|
||||
import org.jclouds.compute.functions.GroupNamingConvention;
|
||||
import org.jclouds.compute.internal.BaseComputeService;
|
||||
import org.jclouds.compute.internal.PersistNodeCredentials;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
|
||||
import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
|
||||
import org.jclouds.compute.strategy.DestroyNodeStrategy;
|
||||
import org.jclouds.compute.strategy.GetImageStrategy;
|
||||
import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
|
||||
import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
|
||||
import org.jclouds.compute.strategy.ListNodesStrategy;
|
||||
import org.jclouds.compute.strategy.RebootNodeStrategy;
|
||||
import org.jclouds.compute.strategy.ResumeNodeStrategy;
|
||||
import org.jclouds.compute.strategy.SuspendNodeStrategy;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
|
||||
import org.jclouds.cloudstack.domain.SecurityGroup;
|
||||
import org.jclouds.cloudstack.domain.SshKeyPair;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.domain.ZoneAndName;
|
||||
import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
|
||||
import org.jclouds.cloudstack.predicates.SecurityGroupPredicates;
|
||||
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class CloudStackComputeService extends BaseComputeService {
|
||||
protected final CloudStackClient client;
|
||||
protected final LoadingCache<ZoneAndName, SecurityGroup> securityGroupMap;
|
||||
protected final LoadingCache<String, SshKeyPair> keyPairCache;
|
||||
protected final Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId;
|
||||
protected final GroupNamingConvention.Factory namingConvention;
|
||||
protected final Supplier<LoadingCache<String, Zone>> zoneIdToZone;
|
||||
|
||||
@Inject
|
||||
protected CloudStackComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
|
||||
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
|
||||
@Memoized Supplier<Set<? extends Location>> locations, ListNodesStrategy listNodesStrategy,
|
||||
GetImageStrategy getImageStrategy, GetNodeMetadataStrategy getNodeMetadataStrategy,
|
||||
CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy,
|
||||
DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy startNodeStrategy,
|
||||
SuspendNodeStrategy stopNodeStrategy, Provider<TemplateBuilder> templateBuilderProvider,
|
||||
@Named("DEFAULT") Provider<TemplateOptions> templateOptionsProvider,
|
||||
@Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> nodeRunning,
|
||||
@Named(TIMEOUT_NODE_TERMINATED) Predicate<AtomicReference<NodeMetadata>> nodeTerminated,
|
||||
@Named(TIMEOUT_NODE_SUSPENDED) Predicate<AtomicReference<NodeMetadata>> nodeSuspended,
|
||||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory,
|
||||
RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
|
||||
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, CloudStackClient client,
|
||||
LoadingCache<ZoneAndName, SecurityGroup> securityGroupMap,
|
||||
LoadingCache<String, SshKeyPair> keyPairCache,
|
||||
Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId,
|
||||
GroupNamingConvention.Factory namingConvention,
|
||||
Supplier<LoadingCache<String, Zone>> zoneIdToZone,
|
||||
Optional<ImageExtension> imageExtension) {
|
||||
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
|
||||
getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
|
||||
startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
|
||||
nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
|
||||
persistNodeCredentials, timeouts, userExecutor, imageExtension);
|
||||
this.zoneIdToZone = zoneIdToZone;
|
||||
this.client = checkNotNull(client, "client");
|
||||
this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
|
||||
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||
this.orphanedGroupsByZoneId = checkNotNull(orphanedGroupsByZoneId, "orphanedGroupsByZoneId");
|
||||
this.namingConvention = checkNotNull(namingConvention, "namingConvention");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanUpIncidentalResourcesOfDeadNodes(Set<? extends NodeMetadata> deadNodes) {
|
||||
Multimap<String, String> zoneToZoneAndGroupNames = orphanedGroupsByZoneId.apply(deadNodes);
|
||||
for (String zoneId : zoneToZoneAndGroupNames.keySet()) {
|
||||
cleanOrphanedGroupsInZone(ImmutableSet.copyOf(zoneToZoneAndGroupNames.get(zoneId)), zoneId);
|
||||
}
|
||||
}
|
||||
|
||||
protected void cleanOrphanedGroupsInZone(Set<String> groups, String zoneId) {
|
||||
cleanupOrphanedSecurityGroupsInZone(groups, zoneId);
|
||||
cleanupOrphanedKeyPairsInZone(groups, zoneId);
|
||||
}
|
||||
|
||||
private void cleanupOrphanedSecurityGroupsInZone(Set<String> groups, String zoneId) {
|
||||
Zone zone = zoneIdToZone.get().getUnchecked(zoneId);
|
||||
|
||||
if (supportsSecurityGroups().apply(zone)) {
|
||||
for (String group : groups) {
|
||||
for (SecurityGroup securityGroup : Iterables.filter(client.getSecurityGroupClient().listSecurityGroups(),
|
||||
SecurityGroupPredicates.nameMatches(namingConvention.create().containsGroup(group)))) {
|
||||
ZoneAndName zoneAndName = ZoneAndName.fromZoneAndName(zoneId, securityGroup.getName());
|
||||
logger.debug(">> deleting securityGroup(%s)", zoneAndName);
|
||||
client.getSecurityGroupClient().deleteSecurityGroup(securityGroup.getId());
|
||||
// TODO: test this clear happens
|
||||
securityGroupMap.invalidate(zoneAndName);
|
||||
logger.debug("<< deleted securityGroup(%s)", zoneAndName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupOrphanedKeyPairsInZone(Set<String> groups, String zoneId) {
|
||||
for (String group : groups) {
|
||||
for (SshKeyPair pair : Iterables.filter(client.getSSHKeyPairClient().listSSHKeyPairs(),
|
||||
nameMatches(namingConvention.create().containsGroup(group)))) {
|
||||
logger.debug(">> deleting keypair(%s)", pair.getName());
|
||||
client.getSSHKeyPairClient().deleteSSHKeyPair(pair.getName());
|
||||
// TODO: test this clear happens
|
||||
keyPairCache.invalidate(pair.getName());
|
||||
logger.debug("<< deleted keypair(%s)", pair.getName());
|
||||
}
|
||||
keyPairCache.invalidate(namingConvention.create().sharedNameForGroup(group));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns template options, except of type {@link CloudStackTemplateOptions}.
|
||||
*/
|
||||
@Override
|
||||
public CloudStackTemplateOptions templateOptions() {
|
||||
return CloudStackTemplateOptions.class.cast(super.templateOptions());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -18,6 +18,7 @@ package org.jclouds.cloudstack.compute.config;
|
|||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
import static org.jclouds.cloudstack.config.CloudStackProperties.AUTO_GENERATE_KEYPAIRS;
|
||||
import static org.jclouds.util.Predicates2.retry;
|
||||
|
||||
import java.util.Map;
|
||||
|
@ -29,11 +30,15 @@ import javax.inject.Named;
|
|||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.compute.CloudStackComputeService;
|
||||
import org.jclouds.cloudstack.compute.functions.OrphanedGroupsByZoneId;
|
||||
import org.jclouds.cloudstack.compute.functions.ServiceOfferingToHardware;
|
||||
import org.jclouds.cloudstack.compute.functions.TemplateToImage;
|
||||
import org.jclouds.cloudstack.compute.functions.TemplateToOperatingSystem;
|
||||
import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata;
|
||||
import org.jclouds.cloudstack.compute.functions.ZoneToLocation;
|
||||
import org.jclouds.cloudstack.compute.loaders.CreateUniqueKeyPair;
|
||||
import org.jclouds.cloudstack.compute.loaders.FindSecurityGroupOrCreate;
|
||||
import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
|
||||
import org.jclouds.cloudstack.compute.strategy.AdvancedNetworkOptionsConverter;
|
||||
import org.jclouds.cloudstack.compute.strategy.BasicNetworkOptionsConverter;
|
||||
|
@ -44,12 +49,17 @@ import org.jclouds.cloudstack.domain.IPForwardingRule;
|
|||
import org.jclouds.cloudstack.domain.Network;
|
||||
import org.jclouds.cloudstack.domain.NetworkType;
|
||||
import org.jclouds.cloudstack.domain.OSType;
|
||||
import org.jclouds.cloudstack.domain.SecurityGroup;
|
||||
import org.jclouds.cloudstack.domain.ServiceOffering;
|
||||
import org.jclouds.cloudstack.domain.SshKeyPair;
|
||||
import org.jclouds.cloudstack.domain.Template;
|
||||
import org.jclouds.cloudstack.domain.User;
|
||||
import org.jclouds.cloudstack.domain.VirtualMachine;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.domain.ZoneAndName;
|
||||
import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
|
||||
import org.jclouds.cloudstack.features.GuestOSClient;
|
||||
import org.jclouds.cloudstack.functions.CreateSecurityGroupIfNeeded;
|
||||
import org.jclouds.cloudstack.functions.GetFirewallRulesByVirtualMachine;
|
||||
import org.jclouds.cloudstack.functions.GetIPForwardingRulesByVirtualMachine;
|
||||
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
|
||||
|
@ -77,8 +87,12 @@ import com.google.common.cache.CacheLoader;
|
|||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
|
||||
/**
|
||||
|
@ -111,12 +125,32 @@ public class CloudStackComputeServiceContextModule extends
|
|||
}).to(GetFirewallRulesByVirtualMachine.class);
|
||||
bind(new TypeLiteral<CacheLoader<String, Zone>>() {
|
||||
}).to(ZoneIdToZone.class);
|
||||
bind(new TypeLiteral<CacheLoader<String, SshKeyPair>>() {
|
||||
}).to(CreateUniqueKeyPair.class);
|
||||
bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() {
|
||||
}).to(ZoneIdToZoneSupplier.class);
|
||||
|
||||
bind(new TypeLiteral<Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup>>() {
|
||||
}).to(CreateSecurityGroupIfNeeded.class);
|
||||
|
||||
bind(new TypeLiteral<CacheLoader<ZoneAndName, SecurityGroup>>() {
|
||||
}).to(FindSecurityGroupOrCreate.class);
|
||||
|
||||
bind(new TypeLiteral<Function<Set<? extends NodeMetadata>, Multimap<String, String>>>() {
|
||||
}).to(OrphanedGroupsByZoneId.class);
|
||||
|
||||
// to have the compute service adapter override default locations
|
||||
install(new LocationsFromComputeServiceAdapterModule<VirtualMachine, ServiceOffering, Template, Zone>(){});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) {
|
||||
return options.as(CloudStackTemplateOptions.class)
|
||||
.generateKeyPair(injector.getInstance(
|
||||
Key.get(boolean.class, Names.named(AUTO_GENERATE_KEYPAIRS))));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Memoized
|
||||
|
@ -185,6 +219,20 @@ public class CloudStackComputeServiceContextModule extends
|
|||
return retry(jobComplete, 1200, 1, 5, SECONDS);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected LoadingCache<String, SshKeyPair> keyPairMap(
|
||||
CacheLoader<String, SshKeyPair> in) {
|
||||
return CacheBuilder.newBuilder().build(in);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected LoadingCache<ZoneAndName, SecurityGroup> securityGroupMap(
|
||||
CacheLoader<ZoneAndName, SecurityGroup> in) {
|
||||
return CacheBuilder.newBuilder().build(in);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected LoadingCache<String, Set<IPForwardingRule>> getIPForwardingRulesByVirtualMachine(
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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.cloudstack.compute.functions;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.common.collect.Sets.filter;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.compute.ComputeService;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.predicates.NodePredicates;
|
||||
import org.jclouds.domain.LocationScope;
|
||||
import org.jclouds.cloudstack.compute.predicates.AllNodesInGroupTerminated;
|
||||
import org.jclouds.cloudstack.domain.ZoneAndName;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class OrphanedGroupsByZoneId implements Function<Set<? extends NodeMetadata>, Multimap<String, String>> {
|
||||
private final Predicate<ZoneAndName> allNodesInGroupTerminated;
|
||||
|
||||
@Inject
|
||||
protected OrphanedGroupsByZoneId(ComputeService computeService) {
|
||||
this(new AllNodesInGroupTerminated(checkNotNull(computeService, "computeService")));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
OrphanedGroupsByZoneId(Predicate<ZoneAndName> allNodesInGroupTerminated) {
|
||||
this.allNodesInGroupTerminated = checkNotNull(allNodesInGroupTerminated, "allNodesInGroupTerminated");
|
||||
}
|
||||
|
||||
public Multimap<String, String> apply(Set<? extends NodeMetadata> deadNodes) {
|
||||
Iterable<? extends NodeMetadata> nodesWithGroup = filter(deadNodes, NodePredicates.hasGroup());
|
||||
Set<ZoneAndName> zoneAndGroupNames = ImmutableSet.copyOf(filter(transform(nodesWithGroup,
|
||||
new Function<NodeMetadata, ZoneAndName>() {
|
||||
|
||||
@Override
|
||||
public ZoneAndName apply(NodeMetadata input) {
|
||||
String zoneId = input.getLocation().getScope() == LocationScope.HOST ? input.getLocation()
|
||||
.getParent().getId() : input.getLocation().getId();
|
||||
return ZoneAndName.fromZoneAndName(zoneId, input.getGroup());
|
||||
}
|
||||
|
||||
}), allNodesInGroupTerminated));
|
||||
Multimap<String, String> zoneToZoneAndGroupNames = Multimaps.transformValues(Multimaps.index(zoneAndGroupNames,
|
||||
ZoneAndName.ZONE_FUNCTION), ZoneAndName.NAME_FUNCTION);
|
||||
return zoneToZoneAndGroupNames;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.cloudstack.compute.loaders;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.domain.AsyncCreateResponse;
|
||||
import org.jclouds.cloudstack.domain.SshKeyPair;
|
||||
import org.jclouds.cloudstack.strategy.BlockUntilJobCompletesAndReturnResult;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
/**
|
||||
* @author Adam Lowe
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
@Singleton
|
||||
public class CreateUniqueKeyPair extends CacheLoader<String, SshKeyPair> {
|
||||
@Resource
|
||||
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||
protected Logger logger = Logger.NULL;
|
||||
protected final CloudStackClient client;
|
||||
|
||||
@Inject
|
||||
public CreateUniqueKeyPair(CloudStackClient client) {
|
||||
this.client = checkNotNull(client, "client");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshKeyPair load(String input) {
|
||||
SshKeyPair keyPair = null;
|
||||
while (keyPair == null) {
|
||||
try {
|
||||
keyPair = client.getSSHKeyPairClient().createSSHKeyPair(input);
|
||||
logger.debug(">> creating SSH key pair with name %s", input);
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("<< created keyPair(%s)", keyPair.getName());
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.cloudstack.compute.loaders;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.domain.SecurityGroup;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.domain.ZoneAndName;
|
||||
import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.util.concurrent.Atomics;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
public class FindSecurityGroupOrCreate extends CacheLoader<ZoneAndName, SecurityGroup> {
|
||||
@Resource
|
||||
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||
protected Logger logger = Logger.NULL;
|
||||
protected final CloudStackClient client;
|
||||
protected final Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup> groupCreator;
|
||||
|
||||
@Inject
|
||||
public FindSecurityGroupOrCreate(CloudStackClient client,
|
||||
Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup> groupCreator) {
|
||||
this.client = checkNotNull(client, "client");
|
||||
this.groupCreator = checkNotNull(groupCreator, "groupCreator");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityGroup load(ZoneAndName in) {
|
||||
SecurityGroup group = client.getSecurityGroupClient().getSecurityGroupByName(in.getName());
|
||||
if (group != null) {
|
||||
return group;
|
||||
} else {
|
||||
return createNewSecurityGroup(in);
|
||||
}
|
||||
}
|
||||
|
||||
private SecurityGroup createNewSecurityGroup(ZoneAndName in) {
|
||||
checkState(
|
||||
checkNotNull(in, "ZoneSecurityGrioupNamePortsCidrs") instanceof ZoneSecurityGroupNamePortsCidrs,
|
||||
"programming error: when issuing get to this cacheloader, you need to pass an instance of ZoneSecurityGroupNamePortsCidrs, not %s",
|
||||
in);
|
||||
ZoneSecurityGroupNamePortsCidrs zoneSecurityGroupNamePortsCidrs = ZoneSecurityGroupNamePortsCidrs.class.cast(in);
|
||||
return groupCreator.apply(zoneSecurityGroupNamePortsCidrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "returnExistingSecurityGroupInZoneOrCreateAsNeeded()";
|
||||
}
|
||||
|
||||
}
|
|
@ -58,7 +58,9 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
|
|||
protected boolean setupStaticNat = true;
|
||||
protected String account;
|
||||
protected String domainId;
|
||||
|
||||
protected boolean generateKeyPair = false;
|
||||
protected boolean generateSecurityGroup = false;
|
||||
|
||||
@Override
|
||||
public CloudStackTemplateOptions clone() {
|
||||
CloudStackTemplateOptions options = new CloudStackTemplateOptions();
|
||||
|
@ -76,6 +78,8 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
|
|||
eTo.ipsToNetworks(this.ipsToNetworks);
|
||||
eTo.ipOnDefaultNetwork(this.ipOnDefaultNetwork);
|
||||
eTo.keyPair(this.keyPair);
|
||||
eTo.generateKeyPair(shouldGenerateKeyPair());
|
||||
eTo.generateSecurityGroup(shouldGenerateSecurityGroup());
|
||||
eTo.account(this.account);
|
||||
eTo.domainId(this.domainId);
|
||||
eTo.setupStaticNat(setupStaticNat);
|
||||
|
@ -102,6 +106,21 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
|
|||
return securityGroupIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #shouldGenerateKeyPair()
|
||||
*/
|
||||
public CloudStackTemplateOptions generateSecurityGroup(boolean enable) {
|
||||
this.generateSecurityGroup = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if auto generation of keypairs is enabled
|
||||
*/
|
||||
public boolean shouldGenerateSecurityGroup() {
|
||||
return generateSecurityGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DeployVirtualMachineOptions#networkId
|
||||
*/
|
||||
|
@ -167,6 +186,21 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
|
|||
return keyPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #shouldGenerateKeyPair()
|
||||
*/
|
||||
public CloudStackTemplateOptions generateKeyPair(boolean enable) {
|
||||
this.generateKeyPair = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if auto generation of keypairs is enabled
|
||||
*/
|
||||
public boolean shouldGenerateKeyPair() {
|
||||
return generateKeyPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DeployVirtualMachineOptions#accountInDomain(String,String)
|
||||
*/
|
||||
|
@ -212,6 +246,13 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
|
|||
return options.securityGroupIds(securityGroupIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see CloudStackTemplateOptions#shouldGenerateSecurityGroup()
|
||||
*/
|
||||
public static CloudStackTemplateOptions generateSecurityGroup(boolean enable) {
|
||||
return new CloudStackTemplateOptions().generateSecurityGroup(enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see CloudStackTemplateOptions#networkId
|
||||
*/
|
||||
|
@ -260,6 +301,13 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
|
|||
return options.keyPair(keyPair);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see CloudStackTemplateOptions#shouldGenerateKeyPair()
|
||||
*/
|
||||
public static CloudStackTemplateOptions generateKeyPair(boolean enable) {
|
||||
return new CloudStackTemplateOptions().generateKeyPair(enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see CloudStackTemplateOptions#account
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.cloudstack.compute.predicates;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Predicates.and;
|
||||
import static com.google.common.collect.Iterables.all;
|
||||
import static org.jclouds.compute.predicates.NodePredicates.TERMINATED;
|
||||
import static org.jclouds.compute.predicates.NodePredicates.inGroup;
|
||||
import static org.jclouds.compute.predicates.NodePredicates.locationId;
|
||||
import static org.jclouds.compute.predicates.NodePredicates.parentLocationId;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.compute.ComputeService;
|
||||
import org.jclouds.compute.domain.ComputeMetadata;
|
||||
import org.jclouds.cloudstack.domain.ZoneAndName;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class AllNodesInGroupTerminated implements Predicate<ZoneAndName> {
|
||||
private final ComputeService computeService;
|
||||
|
||||
|
||||
//TODO: TESTME
|
||||
@Inject
|
||||
public AllNodesInGroupTerminated(ComputeService computeService) {
|
||||
this.computeService = checkNotNull(computeService, "computeService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ZoneAndName input) {
|
||||
// new nodes can have the zone as their location, existing nodes, the parent is the
|
||||
// location
|
||||
return all(computeService.listNodesDetailsMatching(Predicates.<ComputeMetadata> or(locationId(input.getZone()),
|
||||
parentLocationId(input.getZone()))), and(inGroup(input.getName()), TERMINATED));
|
||||
}
|
||||
}
|
|
@ -25,6 +25,8 @@ import static com.google.common.collect.Iterables.get;
|
|||
import static org.jclouds.cloudstack.options.DeployVirtualMachineOptions.Builder.displayName;
|
||||
import static org.jclouds.cloudstack.options.ListTemplatesOptions.Builder.id;
|
||||
import static org.jclouds.cloudstack.predicates.TemplatePredicates.isReady;
|
||||
import static org.jclouds.cloudstack.predicates.ZonePredicates.supportsSecurityGroups;
|
||||
import static org.jclouds.ssh.SshKeys.fingerprintPrivateKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -45,10 +47,14 @@ import org.jclouds.cloudstack.domain.IPForwardingRule;
|
|||
import org.jclouds.cloudstack.domain.Network;
|
||||
import org.jclouds.cloudstack.domain.NetworkType;
|
||||
import org.jclouds.cloudstack.domain.PublicIPAddress;
|
||||
import org.jclouds.cloudstack.domain.SecurityGroup;
|
||||
import org.jclouds.cloudstack.domain.ServiceOffering;
|
||||
import org.jclouds.cloudstack.domain.SshKeyPair;
|
||||
import org.jclouds.cloudstack.domain.Template;
|
||||
import org.jclouds.cloudstack.domain.VirtualMachine;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.domain.ZoneAndName;
|
||||
import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
|
||||
import org.jclouds.cloudstack.functions.CreateFirewallRulesForIP;
|
||||
import org.jclouds.cloudstack.functions.CreatePortForwardingRulesForIP;
|
||||
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
|
||||
|
@ -58,6 +64,7 @@ import org.jclouds.cloudstack.options.ListFirewallRulesOptions;
|
|||
import org.jclouds.cloudstack.strategy.BlockUntilJobCompletesAndReturnResult;
|
||||
import org.jclouds.collect.Memoized;
|
||||
import org.jclouds.compute.ComputeServiceAdapter;
|
||||
import org.jclouds.compute.functions.GroupNamingConvention;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.domain.LoginCredentials;
|
||||
|
@ -96,6 +103,9 @@ public class CloudStackComputeServiceAdapter implements
|
|||
private final Map<String, Credentials> credentialStore;
|
||||
private final Map<NetworkType, ? extends OptionsConverter> optionsConverters;
|
||||
private final Supplier<LoadingCache<String, Zone>> zoneIdToZone;
|
||||
private final LoadingCache<ZoneAndName, SecurityGroup> securityGroupCache;
|
||||
private final LoadingCache<String, SshKeyPair> keyPairCache;
|
||||
private final GroupNamingConvention.Factory namingConvention;
|
||||
|
||||
@Inject
|
||||
public CloudStackComputeServiceAdapter(CloudStackClient client, Predicate<String> jobComplete,
|
||||
|
@ -107,7 +117,10 @@ public class CloudStackComputeServiceAdapter implements
|
|||
LoadingCache<String, Set<IPForwardingRule>> vmToRules,
|
||||
Map<String, Credentials> credentialStore,
|
||||
Map<NetworkType, ? extends OptionsConverter> optionsConverters,
|
||||
Supplier<LoadingCache<String, Zone>> zoneIdToZone) {
|
||||
Supplier<LoadingCache<String, Zone>> zoneIdToZone,
|
||||
LoadingCache<ZoneAndName, SecurityGroup> securityGroupCache,
|
||||
LoadingCache<String, SshKeyPair> keyPairCache,
|
||||
GroupNamingConvention.Factory namingConvention) {
|
||||
this.client = checkNotNull(client, "client");
|
||||
this.jobComplete = checkNotNull(jobComplete, "jobComplete");
|
||||
this.networkSupplier = checkNotNull(networkSupplier, "networkSupplier");
|
||||
|
@ -118,8 +131,11 @@ public class CloudStackComputeServiceAdapter implements
|
|||
this.setupFirewallRulesForIP = checkNotNull(setupFirewallRulesForIP, "setupFirewallRulesForIP");
|
||||
this.vmToRules = checkNotNull(vmToRules, "vmToRules");
|
||||
this.credentialStore = checkNotNull(credentialStore, "credentialStore");
|
||||
this.securityGroupCache = checkNotNull(securityGroupCache, "securityGroupCache");
|
||||
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||
this.optionsConverters = optionsConverters;
|
||||
this.zoneIdToZone = zoneIdToZone;
|
||||
this.namingConvention = namingConvention;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -134,12 +150,7 @@ public class CloudStackComputeServiceAdapter implements
|
|||
Map<String, Network> networks = networkSupplier.get();
|
||||
|
||||
final String zoneId = template.getLocation().getId();
|
||||
Zone zone = null;
|
||||
try {
|
||||
zone = zoneIdToZone.get().get(zoneId);
|
||||
} catch (ExecutionException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
Zone zone = zoneIdToZone.get().getUnchecked(zoneId);
|
||||
|
||||
CloudStackTemplateOptions templateOptions = template.getOptions().as(CloudStackTemplateOptions.class);
|
||||
|
||||
|
@ -163,15 +174,37 @@ public class CloudStackComputeServiceAdapter implements
|
|||
}
|
||||
|
||||
if (templateOptions.getKeyPair() != null) {
|
||||
options.keyPair(templateOptions.getKeyPair());
|
||||
if (templateOptions.getRunScript() != null) {
|
||||
checkArgument(
|
||||
credentialStore.containsKey("keypair#" + templateOptions.getKeyPair()),
|
||||
"no private key configured for: %s; please use options.overrideLoginCredentialWith(rsa_private_text)",
|
||||
templateOptions.getKeyPair());
|
||||
if (templateOptions.getLoginPrivateKey() != null) {
|
||||
String pem = templateOptions.getLoginPrivateKey();
|
||||
SshKeyPair keyPair = SshKeyPair.builder().name(templateOptions.getKeyPair())
|
||||
.fingerprint(fingerprintPrivateKey(pem)).privateKey(pem).build();
|
||||
keyPairCache.asMap().put(keyPair.getName(), keyPair);
|
||||
options.keyPair(keyPair.getName());
|
||||
}
|
||||
} else if (templateOptions.shouldGenerateKeyPair()) {
|
||||
SshKeyPair keyPair = keyPairCache.getUnchecked(namingConvention.create()
|
||||
.sharedNameForGroup(group));
|
||||
keyPairCache.asMap().put(keyPair.getName(), keyPair);
|
||||
templateOptions.keyPair(keyPair.getName());
|
||||
options.keyPair(keyPair.getName());
|
||||
}
|
||||
|
||||
if (supportsSecurityGroups().apply(zone)) {
|
||||
List<Integer> inboundPorts = Ints.asList(templateOptions.getInboundPorts());
|
||||
|
||||
if (templateOptions.getSecurityGroupIds().size() == 0
|
||||
&& inboundPorts.size() > 0
|
||||
&& templateOptions.shouldGenerateSecurityGroup()) {
|
||||
String securityGroupName = namingConvention.create().sharedNameForGroup(group);
|
||||
SecurityGroup sg = securityGroupCache.getUnchecked(ZoneSecurityGroupNamePortsCidrs.builder()
|
||||
.zone(zone.getId())
|
||||
.name(securityGroupName)
|
||||
.ports(ImmutableSet.copyOf(inboundPorts))
|
||||
.cidrs(ImmutableSet.<String> of()).build());
|
||||
options.securityGroupId(sg.getId());
|
||||
}
|
||||
}
|
||||
|
||||
String templateId = template.getImage().getId();
|
||||
String serviceOfferingId = template.getHardware().getId();
|
||||
|
||||
|
|
|
@ -42,4 +42,9 @@ public interface CloudStackProperties {
|
|||
*/
|
||||
public static final String CREDENTIAL_TYPE = "jclouds.cloudstack.credential-type";
|
||||
|
||||
/**
|
||||
* Whenever a node is created, automatically generate keypairs for groups, as needed, also
|
||||
* delete the keypair(s) when the last node in the group is destroyed.
|
||||
*/
|
||||
public static final String AUTO_GENERATE_KEYPAIRS = "jclouds.cloudstack.auto-generate-keypairs";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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.cloudstack.domain;
|
||||
|
||||
import static com.google.common.base.Objects.equal;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Objects.ToStringHelper;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* Helpful when looking for resources by zone and name
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class ZoneAndName {
|
||||
|
||||
public static final Function<ZoneAndName, String> NAME_FUNCTION = new Function<ZoneAndName, String>(){
|
||||
|
||||
@Override
|
||||
public String apply(ZoneAndName input) {
|
||||
return input.getName();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static final Function<ZoneAndName, String> ZONE_FUNCTION = new Function<ZoneAndName, String>(){
|
||||
|
||||
@Override
|
||||
public String apply(ZoneAndName input) {
|
||||
return input.getZone();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static ZoneAndName fromZoneAndName(String zoneId, String name) {
|
||||
return new ZoneAndName(zoneId, name);
|
||||
}
|
||||
|
||||
protected final String zoneId;
|
||||
protected final String name;
|
||||
|
||||
protected ZoneAndName(String zoneId, String name) {
|
||||
this.zoneId = checkNotNull(zoneId, "zoneId");
|
||||
this.name = checkNotNull(name, "name");
|
||||
}
|
||||
|
||||
public String getZone() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
ZoneAndName that = ZoneAndName.class.cast(o);
|
||||
return equal(this.zoneId, that.zoneId) && equal(this.name, that.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(zoneId, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string().toString();
|
||||
}
|
||||
|
||||
protected ToStringHelper string() {
|
||||
return Objects.toStringHelper("").add("zoneId", zoneId).add("name", name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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.cloudstack.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Objects.ToStringHelper;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
public class ZoneSecurityGroupNamePortsCidrs extends ZoneAndName {
|
||||
|
||||
public static Builder<?> builder() {
|
||||
return new ConcreteBuilder();
|
||||
}
|
||||
|
||||
public Builder<?> toBuilder() {
|
||||
return new ConcreteBuilder().fromZoneSecurityGroupNamePortsCidrs(this);
|
||||
}
|
||||
|
||||
public abstract static class Builder<T extends Builder<T>> {
|
||||
protected abstract T self();
|
||||
|
||||
protected String zoneId;
|
||||
protected String name;
|
||||
protected Set<Integer> ports = ImmutableSet.of();
|
||||
protected Set<String> cidrs = ImmutableSet.of();
|
||||
|
||||
/**
|
||||
* @see ZoneSecurityGroupNamePortsCidrs#getZone()
|
||||
*/
|
||||
public T zone(String zoneId) {
|
||||
this.zoneId = zoneId;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ZoneSecurityGroupNamePortsCidrs#getName()
|
||||
*/
|
||||
public T name(String name) {
|
||||
this.name = name;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ZoneSecurityGroupNamePortsCidrs#getPorts()
|
||||
*/
|
||||
public T ports(Set<Integer> ports) {
|
||||
this.ports = ImmutableSet.copyOf(checkNotNull(ports, "ports"));
|
||||
return self();
|
||||
}
|
||||
|
||||
public T ports(Integer... in) {
|
||||
return ports(ImmutableSet.copyOf(in));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ZoneSecurityGroupNamePortsCidrs#getCidrs()
|
||||
*/
|
||||
public T cidrs(Set<String> cidrs) {
|
||||
this.cidrs = ImmutableSet.copyOf(checkNotNull(cidrs, "cidrs"));
|
||||
return self();
|
||||
}
|
||||
|
||||
public T cidrs(String... in) {
|
||||
return cidrs(ImmutableSet.copyOf(in));
|
||||
}
|
||||
|
||||
public ZoneSecurityGroupNamePortsCidrs build() {
|
||||
return new ZoneSecurityGroupNamePortsCidrs(zoneId, name, ports, cidrs);
|
||||
}
|
||||
|
||||
public T fromZoneSecurityGroupNamePortsCidrs(ZoneSecurityGroupNamePortsCidrs in) {
|
||||
return this.zone(in.getZone())
|
||||
.name(in.getName())
|
||||
.ports(in.getPorts())
|
||||
.cidrs(in.getCidrs());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
|
||||
@Override
|
||||
protected ConcreteBuilder self() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Set<Integer> ports;
|
||||
private final Set<String> cidrs;
|
||||
|
||||
protected ZoneSecurityGroupNamePortsCidrs(String zoneId, String name, Set<Integer> ports,
|
||||
Set<String> cidrs) {
|
||||
super(zoneId, name);
|
||||
this.ports = ports == null ? ImmutableSet.<Integer>of() : ImmutableSet.copyOf(ports);
|
||||
this.cidrs = cidrs == null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(cidrs);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the set of ports to open in the security group
|
||||
*/
|
||||
public Set<Integer> getPorts() {
|
||||
return ports;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the set of cidrs to give access to the open ports in the security group
|
||||
*/
|
||||
public Set<String> getCidrs() {
|
||||
return cidrs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(zoneId, name, ports, cidrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
ZoneSecurityGroupNamePortsCidrs that = ZoneSecurityGroupNamePortsCidrs.class.cast(obj);
|
||||
return Objects.equal(this.zoneId, that.zoneId)
|
||||
&& Objects.equal(this.name, that.name)
|
||||
&& Objects.equal(this.ports, that.ports)
|
||||
&& Objects.equal(this.cidrs, that.cidrs);
|
||||
}
|
||||
|
||||
protected ToStringHelper string() {
|
||||
return Objects.toStringHelper(this)
|
||||
.add("zoneId", zoneId).add("name", name).add("ports", ports).add("cidrs", cidrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string().toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +78,18 @@ public interface SecurityGroupAsyncClient {
|
|||
@Fallback(NullOnNotFoundOr404.class)
|
||||
ListenableFuture<SecurityGroup> getSecurityGroup(@QueryParam("id") String id);
|
||||
|
||||
/**
|
||||
* @see SecurityGroupClient#getSecurityGroupByName
|
||||
*/
|
||||
@Named("listSecurityGroups")
|
||||
@GET
|
||||
@QueryParams(keys = { "command", "listAll" }, values = { "listSecurityGroups", "true" })
|
||||
@SelectJson("securitygroup")
|
||||
@OnlyElement
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
ListenableFuture<SecurityGroup> getSecurityGroupByName(@QueryParam("securitygroupname") String securityGroupName);
|
||||
|
||||
/**
|
||||
* @see SecurityGroupClient#createSecurityGroup
|
||||
*/
|
||||
|
|
|
@ -131,6 +131,15 @@ public interface SecurityGroupClient {
|
|||
*/
|
||||
SecurityGroup getSecurityGroup(String id);
|
||||
|
||||
/**
|
||||
* get a specific security group by name
|
||||
*
|
||||
* @param securityGroupName
|
||||
* group to get
|
||||
* @return security group or null if not found
|
||||
*/
|
||||
SecurityGroup getSecurityGroupByName(String securityGroupName);
|
||||
|
||||
/**
|
||||
* Creates a security group
|
||||
*
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.cloudstack.functions;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.find;
|
||||
import static org.jclouds.cloudstack.predicates.SecurityGroupPredicates.nameEquals;
|
||||
import static org.jclouds.cloudstack.predicates.SecurityGroupPredicates.portInRangeForCidr;
|
||||
import static org.jclouds.cloudstack.predicates.ZonePredicates.supportsSecurityGroups;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.domain.IngressRule;
|
||||
import org.jclouds.cloudstack.domain.SecurityGroup;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
@Singleton
|
||||
public class CreateSecurityGroupIfNeeded implements Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup> {
|
||||
@Resource
|
||||
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||
protected Logger logger = Logger.NULL;
|
||||
protected final CloudStackClient client;
|
||||
protected final Supplier<LoadingCache<String, Zone>> zoneIdToZone;
|
||||
protected final Predicate<String> jobComplete;
|
||||
|
||||
@Inject
|
||||
public CreateSecurityGroupIfNeeded(CloudStackClient client,
|
||||
Predicate<String> jobComplete,
|
||||
Supplier<LoadingCache<String, Zone>> zoneIdToZone) {
|
||||
this.client = checkNotNull(client, "client");
|
||||
this.jobComplete = checkNotNull(jobComplete, "jobComplete");
|
||||
this.zoneIdToZone = zoneIdToZone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityGroup apply(ZoneSecurityGroupNamePortsCidrs input) {
|
||||
checkNotNull(input, "input");
|
||||
|
||||
String zoneId = input.getZone();
|
||||
Zone zone = zoneIdToZone.get().getUnchecked(zoneId);
|
||||
|
||||
checkArgument(supportsSecurityGroups().apply(zone),
|
||||
"Security groups are required, but the zone %s does not support security groups", zoneId);
|
||||
logger.debug(">> creating securityGroup %s", input);
|
||||
try {
|
||||
|
||||
SecurityGroup securityGroup = client.getSecurityGroupClient().createSecurityGroup(input.getName());
|
||||
|
||||
logger.debug("<< created securityGroup(%s)", securityGroup);
|
||||
ImmutableSet<String> cidrs;
|
||||
if (input.getCidrs().size() > 0) {
|
||||
cidrs = ImmutableSet.copyOf(input.getCidrs());
|
||||
} else {
|
||||
cidrs = ImmutableSet.of("0.0.0.0/0");
|
||||
}
|
||||
for (int port : input.getPorts()) {
|
||||
authorizeGroupToItselfAndToTCPPortAndCidr(client, securityGroup, port, cidrs);
|
||||
}
|
||||
return securityGroup;
|
||||
} catch (IllegalStateException e) {
|
||||
logger.trace("<< trying to find securityGroup(%s): %s", input, e.getMessage());
|
||||
SecurityGroup group = client.getSecurityGroupClient().getSecurityGroupByName(input.getName());
|
||||
logger.debug("<< reused securityGroup(%s)", group.getId());
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
private void authorizeGroupToItselfAndToTCPPortAndCidr(CloudStackClient client,
|
||||
SecurityGroup securityGroup,
|
||||
int port,
|
||||
Set<String> cidrs) {
|
||||
for (String cidr : cidrs) {
|
||||
logger.debug(">> authorizing securityGroup(%s) permission to %s on port %d", securityGroup, cidr, port);
|
||||
if (!portInRangeForCidr(port, cidr).apply(securityGroup)) {
|
||||
jobComplete.apply(client.getSecurityGroupClient().authorizeIngressPortsToCIDRs(securityGroup.getId(),
|
||||
"TCP",
|
||||
port,
|
||||
port,
|
||||
ImmutableSet.of(cidr)));
|
||||
logger.debug("<< authorized securityGroup(%s) permission to %s on port %d", securityGroup, cidr, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.cloudstack.predicates;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Predicates.alwaysTrue;
|
||||
|
||||
import org.jclouds.cloudstack.domain.IngressRule;
|
||||
import org.jclouds.cloudstack.domain.SecurityGroup;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
public class SecurityGroupPredicates {
|
||||
/**
|
||||
*
|
||||
* @return true, if the security group contains an ingress rule with the given port in the port range
|
||||
*/
|
||||
public static Predicate<SecurityGroup> portInRange(final int port) {
|
||||
return new Predicate<SecurityGroup>() {
|
||||
|
||||
@Override
|
||||
public boolean apply(SecurityGroup group) {
|
||||
return Iterables.any(group.getIngressRules(), new Predicate<IngressRule>() {
|
||||
@Override
|
||||
public boolean apply(IngressRule rule) {
|
||||
return rule.getStartPort() <= port && rule.getEndPort() >= port;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "portInRange(" + port + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return true, if the security group contains an ingress rule with the given cidr
|
||||
*/
|
||||
public static Predicate<SecurityGroup> hasCidr(final String cidr) {
|
||||
return new Predicate<SecurityGroup>() {
|
||||
|
||||
@Override
|
||||
public boolean apply(SecurityGroup group) {
|
||||
return Iterables.any(group.getIngressRules(), new Predicate<IngressRule>() {
|
||||
@Override
|
||||
public boolean apply(IngressRule rule) {
|
||||
return rule.getCIDR() != null
|
||||
&& rule.getCIDR().equals(cidr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "hasCidr(" + cidr + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return true, if the security group contains an ingress rule with the given cidr and the given port in range
|
||||
*/
|
||||
public static Predicate<SecurityGroup> portInRangeForCidr(final int port, final String cidr) {
|
||||
return new Predicate<SecurityGroup>() {
|
||||
|
||||
@Override
|
||||
public boolean apply(SecurityGroup group) {
|
||||
return Iterables.any(group.getIngressRules(), new Predicate<IngressRule>() {
|
||||
@Override
|
||||
public boolean apply(IngressRule rule) {
|
||||
return rule.getCIDR() != null
|
||||
&& rule.getCIDR().equals(cidr)
|
||||
&& rule.getStartPort() <= port
|
||||
&& rule.getEndPort() >= port;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "portInRangeForCidr(" + port + ", " + cidr + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return always returns true.
|
||||
*/
|
||||
public static Predicate<SecurityGroup> any() {
|
||||
return alwaysTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* matches name of the given security group
|
||||
*
|
||||
* @param name
|
||||
* @return predicate that matches name
|
||||
*/
|
||||
public static Predicate<SecurityGroup> nameEquals(final String name) {
|
||||
checkNotNull(name, "name must be defined");
|
||||
|
||||
return new Predicate<SecurityGroup>() {
|
||||
@Override
|
||||
public boolean apply(SecurityGroup ext) {
|
||||
return name.equals(ext.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "nameEquals(" + name + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* matches name of the given security group
|
||||
*
|
||||
* @param name
|
||||
* @return predicate that matches name
|
||||
*/
|
||||
public static Predicate<SecurityGroup> nameMatches(final Predicate<String> name) {
|
||||
checkNotNull(name, "name must be defined");
|
||||
|
||||
return new Predicate<SecurityGroup>() {
|
||||
@Override
|
||||
public boolean apply(SecurityGroup ext) {
|
||||
return name.apply(ext.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "nameMatches(" + name + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.cloudstack.predicates;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.jclouds.cloudstack.domain.SshKeyPair;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
/**
|
||||
* Predicates handy when working with SshKeyPairs
|
||||
*
|
||||
* @author Adam Lowe
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
|
||||
public class SshKeyPairPredicates {
|
||||
|
||||
|
||||
/**
|
||||
* matches name of the given key pair
|
||||
*
|
||||
* @param name
|
||||
* @return predicate that matches name
|
||||
*/
|
||||
public static Predicate<SshKeyPair> nameMatches(final Predicate<String> name) {
|
||||
checkNotNull(name, "name must be defined");
|
||||
|
||||
return new Predicate<SshKeyPair>() {
|
||||
@Override
|
||||
public boolean apply(SshKeyPair ext) {
|
||||
return name.apply(ext.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "nameMatches(" + name + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* matches name of the given keypair starts with the specified prefix
|
||||
*
|
||||
* @param name the prefix you are looking for
|
||||
* @return the predicate
|
||||
*/
|
||||
public static Predicate<SshKeyPair> nameEquals(final String name) {
|
||||
checkNotNull(name, "name must be defined");
|
||||
|
||||
return new Predicate<SshKeyPair>() {
|
||||
@Override
|
||||
public boolean apply(SshKeyPair ext) {
|
||||
return ext.getName() != null && ext.getName().equals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "nameEquals(" + name + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package org.jclouds.cloudstack.compute;
|
|||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jclouds.cloudstack.CloudStackContext;
|
||||
|
@ -33,6 +34,7 @@ import org.jclouds.compute.domain.TemplateBuilder;
|
|||
import org.jclouds.domain.LoginCredentials;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -62,8 +64,26 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
|
|||
HttpResponse queryAsyncJobResultResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/queryasyncjobresultresponse-virtualmachine.json"))
|
||||
.build();
|
||||
|
||||
HttpResponse queryAsyncJobResultSecurityGroupResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/queryasyncjobresultresponse-virtualmachine-securitygroup.json"))
|
||||
.build();
|
||||
|
||||
HttpRequest queryAsyncJobResultAuthorizeIngress = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
.addQueryParam("command", "queryAsyncJobResult")
|
||||
.addQueryParam("jobid", "13330fc9-8b3e-4582-aa3e-90883c041010")
|
||||
.addQueryParam("apiKey", "APIKEY")
|
||||
.addQueryParam("signature", "y4gk3ckWAMPDNZM26LUK0gAhfiE%3D")
|
||||
.addHeader("Accept", "application/json")
|
||||
.build();
|
||||
|
||||
HttpResponse queryAsyncJobResultAuthorizeIngressResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/queryasyncjobresultresponse-authorizeingress.json"))
|
||||
.build();
|
||||
|
||||
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPair() {
|
||||
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPair() throws IOException {
|
||||
HttpRequest deployVM = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
|
@ -95,8 +115,11 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
|
|||
|
||||
Injector forKeyPair = requestsSendResponses(requestResponseMap);
|
||||
|
||||
String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
|
||||
Template template = forKeyPair.getInstance(TemplateBuilder.class).osFamily(OsFamily.CENTOS).build();
|
||||
template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair").setupStaticNat(false);
|
||||
template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair")
|
||||
.setupStaticNat(false)
|
||||
.overrideLoginPrivateKey(privKey);
|
||||
|
||||
CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);
|
||||
|
||||
|
@ -106,7 +129,156 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
|
|||
assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
|
||||
}
|
||||
|
||||
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairAssignedToAccountAndDomain() {
|
||||
public void testCreateNodeWithGroupEncodedIntoNameWithGenerateKeyPair() throws IOException {
|
||||
HttpRequest deployVM = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
.addQueryParam("command", "deployVirtualMachine")
|
||||
.addQueryParam("zoneid", "1")
|
||||
.addQueryParam("serviceofferingid", "1")
|
||||
.addQueryParam("templateid", "4")
|
||||
.addQueryParam("displayname", "test-e92")
|
||||
.addQueryParam("name", "test-e92")
|
||||
.addQueryParam("networkids", "204")
|
||||
.addQueryParam("keypair", "jclouds-test")
|
||||
.addQueryParam("apiKey", "APIKEY")
|
||||
.addQueryParam("signature", "4M9C8IjohDDKFMAXQSX3mjXpYvM%3D")
|
||||
.addHeader("Accept", "application/json")
|
||||
.build();
|
||||
|
||||
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
|
||||
.put(listTemplates, listTemplatesResponse)
|
||||
.put(listOsTypes, listOsTypesResponse)
|
||||
.put(listOsCategories, listOsCategoriesResponse)
|
||||
.put(listZones, listZonesResponse)
|
||||
.put(listServiceOfferings, listServiceOfferingsResponse)
|
||||
.put(listAccounts, listAccountsResponse)
|
||||
.put(listNetworks, listNetworksResponse)
|
||||
.put(getZone, getZoneResponse)
|
||||
.put(deployVM, deployVMResponse)
|
||||
.put(createSSHKeyPair, createSSHKeyPairResponse)
|
||||
.put(queryAsyncJobResult, queryAsyncJobResultResponse)
|
||||
.build();
|
||||
|
||||
Injector forKeyPair = requestsSendResponses(requestResponseMap);
|
||||
|
||||
Template template = forKeyPair.getInstance(TemplateBuilder.class).osFamily(OsFamily.CENTOS).build();
|
||||
template.getOptions().as(CloudStackTemplateOptions.class).generateKeyPair(true)
|
||||
.setupStaticNat(false);
|
||||
|
||||
CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);
|
||||
|
||||
NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
|
||||
template);
|
||||
assertNotNull(server);
|
||||
assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
|
||||
}
|
||||
|
||||
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairDefaultSecurityGroup() throws IOException {
|
||||
HttpRequest deployVM = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
.addQueryParam("command", "deployVirtualMachine")
|
||||
.addQueryParam("zoneid", "2")
|
||||
.addQueryParam("serviceofferingid", "1")
|
||||
.addQueryParam("templateid", "241")
|
||||
.addQueryParam("displayname", "test-e92")
|
||||
.addQueryParam("name", "test-e92")
|
||||
.addQueryParam("networkids", "204")
|
||||
.addQueryParam("keypair", "mykeypair")
|
||||
.addQueryParam("apiKey", "APIKEY")
|
||||
.addQueryParam("signature", "Ar2B/ZVxMO2078cP0XliWWR4cQ0%3D")
|
||||
.addHeader("Accept", "application/json")
|
||||
.build();
|
||||
|
||||
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
|
||||
.put(listTemplates, listTemplatesResponse)
|
||||
.put(listOsTypes, listOsTypesResponse)
|
||||
.put(listOsCategories, listOsCategoriesResponse)
|
||||
.put(listZones, listZonesResponse)
|
||||
.put(listServiceOfferings, listServiceOfferingsResponse)
|
||||
.put(listAccounts, listAccountsResponse)
|
||||
.put(listNetworks, listNetworksWithSecurityGroupsResponse)
|
||||
.put(getZoneWithSecurityGroups, getZoneWithSecurityGroupsResponse)
|
||||
.put(deployVM, deployVMResponse)
|
||||
.put(queryAsyncJobResult, queryAsyncJobResultResponse)
|
||||
.build();
|
||||
|
||||
Injector forKeyPair = requestsSendResponses(requestResponseMap);
|
||||
|
||||
String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
|
||||
Template template = forKeyPair.getInstance(TemplateBuilder.class)
|
||||
.osFamily(OsFamily.CENTOS)
|
||||
.locationId("2")
|
||||
.build();
|
||||
template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair")
|
||||
.setupStaticNat(false)
|
||||
.overrideLoginPrivateKey(privKey);
|
||||
|
||||
CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);
|
||||
|
||||
NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
|
||||
template);
|
||||
assertNotNull(server);
|
||||
assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
|
||||
}
|
||||
|
||||
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairGenerateSecurityGroup() throws IOException {
|
||||
HttpRequest deployVM = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
.addQueryParam("command", "deployVirtualMachine")
|
||||
.addQueryParam("zoneid", "2")
|
||||
.addQueryParam("serviceofferingid", "1")
|
||||
.addQueryParam("templateid", "241")
|
||||
.addQueryParam("displayname", "test-e92")
|
||||
.addQueryParam("name", "test-e92")
|
||||
.addQueryParam("networkids", "204")
|
||||
.addQueryParam("keypair", "mykeypair")
|
||||
.addQueryParam("securitygroupids", "30")
|
||||
.addQueryParam("apiKey", "APIKEY")
|
||||
.addQueryParam("signature", "yNAiMYw3RstNj979udttALOHxfU%3D")
|
||||
.addHeader("Accept", "application/json")
|
||||
.build();
|
||||
|
||||
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
|
||||
.put(listTemplates, listTemplatesResponse)
|
||||
.put(listOsTypes, listOsTypesResponse)
|
||||
.put(listOsCategories, listOsCategoriesResponse)
|
||||
.put(listZones, listZonesResponse)
|
||||
.put(listServiceOfferings, listServiceOfferingsResponse)
|
||||
.put(listAccounts, listAccountsResponse)
|
||||
.put(listNetworks, listNetworksWithSecurityGroupsResponse)
|
||||
.put(getZoneWithSecurityGroups, getZoneWithSecurityGroupsResponse)
|
||||
.put(deployVM, deployVMResponse)
|
||||
.put(queryAsyncJobResult, queryAsyncJobResultSecurityGroupResponse)
|
||||
.put(queryAsyncJobResultAuthorizeIngress, queryAsyncJobResultAuthorizeIngressResponse)
|
||||
.put(getSecurityGroup, getSecurityGroupResponse)
|
||||
.put(createSecurityGroup, createSecurityGroupResponse)
|
||||
.put(authorizeIngress, authorizeIngressResponse)
|
||||
.build();
|
||||
|
||||
Injector forKeyPair = requestsSendResponses(requestResponseMap);
|
||||
|
||||
String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
|
||||
Template template = forKeyPair.getInstance(TemplateBuilder.class)
|
||||
.osFamily(OsFamily.CENTOS)
|
||||
.locationId("2")
|
||||
.build();
|
||||
template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair")
|
||||
.setupStaticNat(false)
|
||||
.generateSecurityGroup(true)
|
||||
.overrideLoginPrivateKey(privKey);
|
||||
|
||||
CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);
|
||||
|
||||
NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
|
||||
template);
|
||||
assertNotNull(server);
|
||||
assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
|
||||
}
|
||||
|
||||
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairAssignedToAccountAndDomain() throws IOException {
|
||||
HttpRequest deployVM = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
|
@ -140,8 +312,13 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
|
|||
|
||||
Injector forKeyPair = requestsSendResponses(requestResponseMap);
|
||||
|
||||
String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
|
||||
Template template = forKeyPair.getInstance(TemplateBuilder.class).osFamily(OsFamily.CENTOS).build();
|
||||
template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair").account("account").domainId("domainId").setupStaticNat(false);
|
||||
template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair")
|
||||
.account("account")
|
||||
.domainId("domainId")
|
||||
.setupStaticNat(false)
|
||||
.overrideLoginPrivateKey(privKey);
|
||||
|
||||
CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.cloudstack.compute.loaders;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.domain.SshKeyPair;
|
||||
import org.jclouds.cloudstack.features.SSHKeyPairClient;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
* @author Adam Lowe
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
@Test(groups = "unit", testName = "CreateUniqueKeyPairTest")
|
||||
public class CreateUniqueKeyPairTest {
|
||||
|
||||
@Test
|
||||
public void testLoad() throws UnknownHostException {
|
||||
final CloudStackClient client = createMock(CloudStackClient.class);
|
||||
SSHKeyPairClient keyClient = createMock(SSHKeyPairClient.class);
|
||||
|
||||
SshKeyPair pair = createMock(SshKeyPair.class);
|
||||
|
||||
expect(client.getSSHKeyPairClient()).andReturn(keyClient);
|
||||
expect(keyClient.createSSHKeyPair("group-1")).andReturn(pair);
|
||||
|
||||
replay(client, keyClient);
|
||||
|
||||
CreateUniqueKeyPair parser = Guice.createInjector(new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(Suppliers.ofInstance("1"));
|
||||
bind(CloudStackClient.class).toInstance(client);
|
||||
}
|
||||
|
||||
}).getInstance(CreateUniqueKeyPair.class);
|
||||
|
||||
assertEquals(parser.load("group-1"), pair);
|
||||
|
||||
verify(client, keyClient);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* 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.cloudstack.compute.loaders;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
import static org.jclouds.util.Predicates2.retry;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.domain.IngressRule;
|
||||
import org.jclouds.cloudstack.domain.SecurityGroup;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.domain.ZoneAndName;
|
||||
import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
|
||||
import org.jclouds.cloudstack.features.AsyncJobClient;
|
||||
import org.jclouds.cloudstack.features.SecurityGroupClient;
|
||||
import org.jclouds.cloudstack.features.ZoneClient;
|
||||
import org.jclouds.cloudstack.functions.CreateSecurityGroupIfNeeded;
|
||||
import org.jclouds.cloudstack.functions.ZoneIdToZone;
|
||||
import org.jclouds.cloudstack.predicates.JobComplete;
|
||||
import org.jclouds.cloudstack.suppliers.ZoneIdToZoneSupplier;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
/**
|
||||
* @author Adam Lowe
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
@Test(groups = "unit", testName = "FindSecurityGroupOrCreateTest")
|
||||
public class FindSecurityGroupOrCreateTest {
|
||||
|
||||
@Test
|
||||
public void testLoad() throws UnknownHostException {
|
||||
final CloudStackClient client = createMock(CloudStackClient.class);
|
||||
SecurityGroupClient secClient = createMock(SecurityGroupClient.class);
|
||||
ZoneClient zoneClient = createMock(ZoneClient.class);
|
||||
AsyncJobClient jobClient = createMock(AsyncJobClient.class);
|
||||
|
||||
SecurityGroup group = createMock(SecurityGroup.class);
|
||||
|
||||
Zone zone = createMock(Zone.class);
|
||||
|
||||
expect(group.getIngressRules()).andReturn(ImmutableSet.<IngressRule> of());
|
||||
expect(group.getId()).andReturn("sec-1234").anyTimes();
|
||||
expect(zone.isSecurityGroupsEnabled()).andReturn(true);
|
||||
|
||||
expect(client.getSecurityGroupClient()).andReturn(secClient)
|
||||
.anyTimes();
|
||||
expect(client.getZoneClient()).andReturn(zoneClient);
|
||||
expect(client.getAsyncJobClient()).andReturn(jobClient).anyTimes();
|
||||
|
||||
expect(zoneClient.getZone("zone-1")).andReturn(zone);
|
||||
expect(secClient.getSecurityGroupByName("group-1")).andReturn(null);
|
||||
expect(secClient.createSecurityGroup("group-1")).andReturn(group);
|
||||
expect(secClient.authorizeIngressPortsToCIDRs("sec-1234",
|
||||
"TCP",
|
||||
22,
|
||||
22,
|
||||
ImmutableSet.of("0.0.0.0/0"))).andReturn("job-1234");
|
||||
|
||||
replay(client, secClient, zoneClient, zone, group);
|
||||
|
||||
ZoneSecurityGroupNamePortsCidrs input = ZoneSecurityGroupNamePortsCidrs.builder()
|
||||
.zone("zone-1")
|
||||
.name("group-1")
|
||||
.ports(ImmutableSet.of(22))
|
||||
.cidrs(ImmutableSet.<String> of()).build();
|
||||
|
||||
FindSecurityGroupOrCreate parser = Guice.createInjector(new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(Suppliers.ofInstance("1"));
|
||||
bind(CloudStackClient.class).toInstance(client);
|
||||
bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
|
||||
to(ZoneIdToZone.class);
|
||||
bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() {}).
|
||||
to(ZoneIdToZoneSupplier.class);
|
||||
bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
|
||||
|
||||
bind(new TypeLiteral<Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup>>() {
|
||||
}).to(CreateSecurityGroupIfNeeded.class);
|
||||
|
||||
bind(new TypeLiteral<CacheLoader<ZoneAndName, SecurityGroup>>() {
|
||||
}).to(FindSecurityGroupOrCreate.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected Predicate<String> jobComplete(JobComplete jobComplete) {
|
||||
return retry(jobComplete, 1200, 1, 5, SECONDS);
|
||||
}
|
||||
|
||||
}).getInstance(FindSecurityGroupOrCreate.class);
|
||||
|
||||
assertEquals(parser.load(input), group);
|
||||
|
||||
verify(client, secClient, zoneClient, zone, group);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLoadAlreadyExists() throws UnknownHostException {
|
||||
final CloudStackClient client = createMock(CloudStackClient.class);
|
||||
SecurityGroupClient secClient = createMock(SecurityGroupClient.class);
|
||||
ZoneClient zoneClient = createMock(ZoneClient.class);
|
||||
AsyncJobClient jobClient = createMock(AsyncJobClient.class);
|
||||
|
||||
SecurityGroup group = createMock(SecurityGroup.class);
|
||||
|
||||
Zone zone = createMock(Zone.class);
|
||||
|
||||
expect(group.getId()).andReturn("sec-1234").anyTimes();
|
||||
|
||||
expect(client.getSecurityGroupClient()).andReturn(secClient)
|
||||
.anyTimes();
|
||||
expect(client.getZoneClient()).andReturn(zoneClient);
|
||||
expect(client.getAsyncJobClient()).andReturn(jobClient).anyTimes();
|
||||
|
||||
expect(secClient.getSecurityGroupByName("group-1")).andReturn(group);
|
||||
|
||||
replay(client, secClient, zoneClient, zone, group);
|
||||
|
||||
ZoneSecurityGroupNamePortsCidrs input = ZoneSecurityGroupNamePortsCidrs.builder()
|
||||
.zone("zone-1")
|
||||
.name("group-1")
|
||||
.ports(ImmutableSet.of(22))
|
||||
.cidrs(ImmutableSet.<String> of()).build();
|
||||
|
||||
FindSecurityGroupOrCreate parser = Guice.createInjector(new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(Suppliers.ofInstance("1"));
|
||||
bind(CloudStackClient.class).toInstance(client);
|
||||
bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
|
||||
to(ZoneIdToZone.class);
|
||||
bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() {}).
|
||||
to(ZoneIdToZoneSupplier.class);
|
||||
bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
|
||||
|
||||
bind(new TypeLiteral<Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup>>() {
|
||||
}).to(CreateSecurityGroupIfNeeded.class);
|
||||
|
||||
bind(new TypeLiteral<CacheLoader<ZoneAndName, SecurityGroup>>() {
|
||||
}).to(FindSecurityGroupOrCreate.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected Predicate<String> jobComplete(JobComplete jobComplete) {
|
||||
return retry(jobComplete, 1200, 1, 5, SECONDS);
|
||||
}
|
||||
|
||||
}).getInstance(FindSecurityGroupOrCreate.class);
|
||||
|
||||
assertEquals(parser.load(input), group);
|
||||
|
||||
verify(client, secClient, zoneClient, zone, group);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testLoadZoneNoSecurityGroups() throws UnknownHostException {
|
||||
final CloudStackClient client = createMock(CloudStackClient.class);
|
||||
SecurityGroupClient secClient = createMock(SecurityGroupClient.class);
|
||||
ZoneClient zoneClient = createMock(ZoneClient.class);
|
||||
AsyncJobClient jobClient = createMock(AsyncJobClient.class);
|
||||
|
||||
SecurityGroup group = createMock(SecurityGroup.class);
|
||||
|
||||
Zone zone = createMock(Zone.class);
|
||||
|
||||
expect(zone.isSecurityGroupsEnabled()).andReturn(false);
|
||||
|
||||
expect(client.getSecurityGroupClient()).andReturn(secClient)
|
||||
.anyTimes();
|
||||
expect(client.getZoneClient()).andReturn(zoneClient);
|
||||
expect(client.getAsyncJobClient()).andReturn(jobClient).anyTimes();
|
||||
|
||||
expect(zoneClient.getZone("zone-1")).andReturn(zone);
|
||||
expect(secClient.getSecurityGroupByName("group-1")).andReturn(null);
|
||||
|
||||
replay(client, secClient, zoneClient, zone, group);
|
||||
|
||||
ZoneSecurityGroupNamePortsCidrs input = ZoneSecurityGroupNamePortsCidrs.builder()
|
||||
.zone("zone-1")
|
||||
.name("group-1")
|
||||
.ports(ImmutableSet.of(22))
|
||||
.cidrs(ImmutableSet.<String> of()).build();
|
||||
|
||||
FindSecurityGroupOrCreate parser = Guice.createInjector(new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(Suppliers.ofInstance("1"));
|
||||
bind(CloudStackClient.class).toInstance(client);
|
||||
bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
|
||||
to(ZoneIdToZone.class);
|
||||
bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() {}).
|
||||
to(ZoneIdToZoneSupplier.class);
|
||||
bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
|
||||
|
||||
bind(new TypeLiteral<Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup>>() {
|
||||
}).to(CreateSecurityGroupIfNeeded.class);
|
||||
|
||||
bind(new TypeLiteral<CacheLoader<ZoneAndName, SecurityGroup>>() {
|
||||
}).to(FindSecurityGroupOrCreate.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected Predicate<String> jobComplete(JobComplete jobComplete) {
|
||||
return retry(jobComplete, 1200, 1, 5, SECONDS);
|
||||
}
|
||||
|
||||
}).getInstance(FindSecurityGroupOrCreate.class);
|
||||
|
||||
assertEquals(parser.load(input), group);
|
||||
|
||||
verify(client, secClient, zoneClient, zone, group);
|
||||
}
|
||||
|
||||
}
|
|
@ -18,6 +18,8 @@ package org.jclouds.cloudstack.compute.options;
|
|||
|
||||
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.account;
|
||||
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.domainId;
|
||||
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.generateKeyPair;
|
||||
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.generateSecurityGroup;
|
||||
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.ipOnDefaultNetwork;
|
||||
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.ipsToNetworks;
|
||||
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.keyPair;
|
||||
|
@ -87,6 +89,27 @@ public class CloudStackTemplateOptionsTest {
|
|||
assertEquals(options.as(CloudStackTemplateOptions.class).getSecurityGroupIds(), ImmutableSet.of("3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateSecurityGroupDefaultsFalse() {
|
||||
TemplateOptions options = new CloudStackTemplateOptions();
|
||||
assertFalse(options.as(CloudStackTemplateOptions.class)
|
||||
.shouldGenerateSecurityGroup());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateSecurityGroup() {
|
||||
TemplateOptions options = new CloudStackTemplateOptions().generateSecurityGroup(true);
|
||||
assertTrue(options.as(CloudStackTemplateOptions.class)
|
||||
.shouldGenerateSecurityGroup());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateSecurityGroupStatic() {
|
||||
TemplateOptions options = generateSecurityGroup(true);
|
||||
assertTrue(options.as(CloudStackTemplateOptions.class)
|
||||
.shouldGenerateSecurityGroup());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultNetworkIds() {
|
||||
TemplateOptions options = new CloudStackTemplateOptions();
|
||||
|
@ -170,6 +193,27 @@ public class CloudStackTemplateOptionsTest {
|
|||
.shouldSetupStaticNat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateKeyPairDefaultsFalse() {
|
||||
TemplateOptions options = new CloudStackTemplateOptions();
|
||||
assertFalse(options.as(CloudStackTemplateOptions.class)
|
||||
.shouldGenerateKeyPair());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateKeyPair() {
|
||||
TemplateOptions options = new CloudStackTemplateOptions().generateKeyPair(true);
|
||||
assertTrue(options.as(CloudStackTemplateOptions.class)
|
||||
.shouldGenerateKeyPair());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateKeyPairStatic() {
|
||||
TemplateOptions options = generateKeyPair(true);
|
||||
assertTrue(options.as(CloudStackTemplateOptions.class)
|
||||
.shouldGenerateKeyPair());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyPair() {
|
||||
TemplateOptions options = keyPair("test");
|
||||
|
|
|
@ -104,6 +104,24 @@ public class SecurityGroupAsyncClientTest extends BaseCloudStackAsyncClientTest<
|
|||
|
||||
}
|
||||
|
||||
public void testGetSecurityGroupByName() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = method(SecurityGroupAsyncClient.class, "getSecurityGroupByName", String.class);
|
||||
GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("some-name"));
|
||||
|
||||
assertRequestLineEquals(httpRequest,
|
||||
"GET http://localhost:8080/client/api?response=json&command=listSecurityGroups&listAll=true&securitygroupname=some-name HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
|
||||
assertPayloadEquals(httpRequest, null, null, false);
|
||||
|
||||
assertResponseParserClassEquals(method, httpRequest,
|
||||
Functions.compose(IdentityFunction.INSTANCE, IdentityFunction.INSTANCE).getClass());
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
|
||||
|
||||
checkFilters(httpRequest);
|
||||
|
||||
}
|
||||
|
||||
public void testCreateSecurityGroup() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = method(SecurityGroupAsyncClient.class, "createSecurityGroup", String.class);
|
||||
GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("goo"));
|
||||
|
|
|
@ -190,8 +190,10 @@ public class SecurityGroupClientLiveTest extends BaseCloudStackClientLiveTest {
|
|||
|
||||
protected void checkGroup(SecurityGroup group) {
|
||||
// http://bugs.cloud.com/show_bug.cgi?id=8968
|
||||
if (group.getIngressRules().size() <= 1)
|
||||
if (group.getIngressRules().size() <= 1) {
|
||||
assertEquals(group, client.getSecurityGroupClient().getSecurityGroup(group.getId()));
|
||||
assertEquals(group, client.getSecurityGroupClient().getSecurityGroupByName(group.getName()));
|
||||
}
|
||||
assert group.getId() != null : group;
|
||||
assert group.getName() != null : group;
|
||||
assert group.getAccount() != null : group;
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* 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.cloudstack.functions;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
import static org.jclouds.util.Predicates2.retry;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.domain.IngressRule;
|
||||
import org.jclouds.cloudstack.domain.SecurityGroup;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs;
|
||||
import org.jclouds.cloudstack.features.AsyncJobClient;
|
||||
import org.jclouds.cloudstack.features.SecurityGroupClient;
|
||||
import org.jclouds.cloudstack.features.ZoneClient;
|
||||
import org.jclouds.cloudstack.functions.ZoneIdToZone;
|
||||
import org.jclouds.cloudstack.predicates.JobComplete;
|
||||
import org.jclouds.cloudstack.suppliers.ZoneIdToZoneSupplier;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
/**
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
@Test(groups = "unit", testName = "CreateSecurityGroupIfNeededTest")
|
||||
public class CreateSecurityGroupIfNeededTest {
|
||||
|
||||
@Test
|
||||
public void testApply() throws UnknownHostException {
|
||||
final CloudStackClient client = createMock(CloudStackClient.class);
|
||||
SecurityGroupClient secClient = createMock(SecurityGroupClient.class);
|
||||
ZoneClient zoneClient = createMock(ZoneClient.class);
|
||||
AsyncJobClient jobClient = createMock(AsyncJobClient.class);
|
||||
|
||||
SecurityGroup group = createMock(SecurityGroup.class);
|
||||
|
||||
Zone zone = createMock(Zone.class);
|
||||
|
||||
expect(group.getIngressRules()).andReturn(ImmutableSet.<IngressRule> of());
|
||||
expect(group.getId()).andReturn("sec-1234").anyTimes();
|
||||
expect(zone.isSecurityGroupsEnabled()).andReturn(true);
|
||||
|
||||
expect(client.getSecurityGroupClient()).andReturn(secClient)
|
||||
.anyTimes();
|
||||
expect(client.getZoneClient()).andReturn(zoneClient);
|
||||
expect(client.getAsyncJobClient()).andReturn(jobClient).anyTimes();
|
||||
|
||||
expect(zoneClient.getZone("zone-abc1")).andReturn(zone);
|
||||
expect(secClient.createSecurityGroup("group-1")).andReturn(group);
|
||||
expect(secClient.authorizeIngressPortsToCIDRs("sec-1234",
|
||||
"TCP",
|
||||
22,
|
||||
22,
|
||||
ImmutableSet.of("0.0.0.0/0"))).andReturn("job-1234");
|
||||
|
||||
replay(client, secClient, zoneClient, zone, group);
|
||||
|
||||
ZoneSecurityGroupNamePortsCidrs input = ZoneSecurityGroupNamePortsCidrs.builder()
|
||||
.zone("zone-abc1")
|
||||
.name("group-1")
|
||||
.ports(ImmutableSet.of(22))
|
||||
.cidrs(ImmutableSet.<String> of()).build();
|
||||
|
||||
CreateSecurityGroupIfNeeded parser = Guice.createInjector(new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(Suppliers.ofInstance("1"));
|
||||
bind(CloudStackClient.class).toInstance(client);
|
||||
bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
|
||||
to(ZoneIdToZone.class);
|
||||
bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() {}).
|
||||
to(ZoneIdToZoneSupplier.class);
|
||||
bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected Predicate<String> jobComplete(JobComplete jobComplete) {
|
||||
return retry(jobComplete, 1200, 1, 5, SECONDS);
|
||||
}
|
||||
|
||||
}).getInstance(CreateSecurityGroupIfNeeded.class);
|
||||
|
||||
assertEquals(parser.apply(input), group);
|
||||
|
||||
verify(client, secClient, zoneClient, zone, group);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testApplyGroupAlreadyExists() throws UnknownHostException {
|
||||
final CloudStackClient client = createMock(CloudStackClient.class);
|
||||
SecurityGroupClient secClient = createMock(SecurityGroupClient.class);
|
||||
ZoneClient zoneClient = createMock(ZoneClient.class);
|
||||
AsyncJobClient jobClient = createMock(AsyncJobClient.class);
|
||||
|
||||
SecurityGroup group = createMock(SecurityGroup.class);
|
||||
|
||||
Zone zone = createMock(Zone.class);
|
||||
|
||||
expect(group.getId()).andReturn("sec-1234").anyTimes();
|
||||
expect(zone.isSecurityGroupsEnabled()).andReturn(true);
|
||||
|
||||
expect(client.getSecurityGroupClient()).andReturn(secClient)
|
||||
.anyTimes();
|
||||
expect(client.getZoneClient()).andReturn(zoneClient);
|
||||
expect(client.getAsyncJobClient()).andReturn(jobClient).anyTimes();
|
||||
|
||||
expect(zoneClient.getZone("zone-abc2")).andReturn(zone);
|
||||
expect(secClient.createSecurityGroup("group-1")).andThrow(new IllegalStateException());
|
||||
expect(secClient.getSecurityGroupByName("group-1")).andReturn(group);
|
||||
|
||||
replay(client, secClient, zoneClient, zone, group);
|
||||
|
||||
ZoneSecurityGroupNamePortsCidrs input = ZoneSecurityGroupNamePortsCidrs.builder()
|
||||
.zone("zone-abc2")
|
||||
.name("group-1")
|
||||
.ports(ImmutableSet.of(22))
|
||||
.cidrs(ImmutableSet.<String> of()).build();
|
||||
|
||||
CreateSecurityGroupIfNeeded parser = Guice.createInjector(new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(Suppliers.ofInstance("1"));
|
||||
bind(CloudStackClient.class).toInstance(client);
|
||||
bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
|
||||
to(ZoneIdToZone.class);
|
||||
bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() {}).
|
||||
to(ZoneIdToZoneSupplier.class);
|
||||
bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected Predicate<String> jobComplete(JobComplete jobComplete) {
|
||||
return retry(jobComplete, 1200, 1, 5, SECONDS);
|
||||
}
|
||||
|
||||
}).getInstance(CreateSecurityGroupIfNeeded.class);
|
||||
|
||||
assertEquals(parser.apply(input), group);
|
||||
|
||||
verify(client, secClient, zoneClient, zone, group);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testApplyZoneNoSecurityGroups() throws UnknownHostException {
|
||||
final CloudStackClient client = createMock(CloudStackClient.class);
|
||||
SecurityGroupClient secClient = createMock(SecurityGroupClient.class);
|
||||
ZoneClient zoneClient = createMock(ZoneClient.class);
|
||||
AsyncJobClient jobClient = createMock(AsyncJobClient.class);
|
||||
|
||||
SecurityGroup group = createMock(SecurityGroup.class);
|
||||
|
||||
Zone zone = createMock(Zone.class);
|
||||
|
||||
expect(zone.isSecurityGroupsEnabled()).andReturn(false);
|
||||
|
||||
expect(client.getZoneClient()).andReturn(zoneClient);
|
||||
|
||||
expect(zoneClient.getZone("zone-abc3")).andReturn(zone);
|
||||
|
||||
replay(client, zoneClient, zone);
|
||||
|
||||
ZoneSecurityGroupNamePortsCidrs input = ZoneSecurityGroupNamePortsCidrs.builder()
|
||||
.zone("zone-abc3")
|
||||
.name("group-1")
|
||||
.ports(ImmutableSet.of(22))
|
||||
.cidrs(ImmutableSet.<String> of()).build();
|
||||
|
||||
CreateSecurityGroupIfNeeded parser = Guice.createInjector(new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(Suppliers.ofInstance("1"));
|
||||
bind(CloudStackClient.class).toInstance(client);
|
||||
bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
|
||||
to(ZoneIdToZone.class);
|
||||
bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() {}).
|
||||
to(ZoneIdToZoneSupplier.class);
|
||||
bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected Predicate<String> jobComplete(JobComplete jobComplete) {
|
||||
return retry(jobComplete, 1200, 1, 5, SECONDS);
|
||||
}
|
||||
|
||||
}).getInstance(CreateSecurityGroupIfNeeded.class);
|
||||
|
||||
assertEquals(parser.apply(input), group);
|
||||
|
||||
verify(client, zoneClient, zone);
|
||||
}
|
||||
|
||||
}
|
|
@ -134,6 +134,10 @@ public abstract class BaseCloudStackComputeServiceContextExpectTest<T> extends B
|
|||
.payload(payloadFromResource("/listnetworksresponse.json"))
|
||||
.build();
|
||||
|
||||
protected final HttpResponse listNetworksWithSecurityGroupsResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/listnetworksresponse-2.json"))
|
||||
.build();
|
||||
|
||||
protected final HttpRequest getZone = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
|
@ -148,6 +152,21 @@ public abstract class BaseCloudStackComputeServiceContextExpectTest<T> extends B
|
|||
protected final HttpResponse getZoneResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/getzoneresponse.json"))
|
||||
.build();
|
||||
|
||||
protected final HttpRequest getZoneWithSecurityGroups = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
.addQueryParam("command", "listZones")
|
||||
.addQueryParam("listAll", "true")
|
||||
.addQueryParam("id", "2")
|
||||
.addQueryParam("apiKey", "APIKEY")
|
||||
.addQueryParam("signature", "FnYX25207fVLLRz5GhOfRrWuUek%3D")
|
||||
.addHeader("Accept", "application/json")
|
||||
.build();
|
||||
|
||||
protected final HttpResponse getZoneWithSecurityGroupsResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/getzoneresponse-2.json"))
|
||||
.build();
|
||||
|
||||
protected final HttpRequest listCapabilities = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
|
@ -163,4 +182,64 @@ public abstract class BaseCloudStackComputeServiceContextExpectTest<T> extends B
|
|||
.payload(payloadFromResource("/listcapabilitiesresponse.json"))
|
||||
.build();
|
||||
|
||||
protected final HttpRequest getSecurityGroup = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
.addQueryParam("command", "listSecurityGroups")
|
||||
.addQueryParam("listAll", "true")
|
||||
.addQueryParam("securitygroupname", "jclouds-test")
|
||||
.addQueryParam("apiKey", "APIKEY")
|
||||
.addQueryParam("signature", "zGp2rfHY6fBIGkgODRxyNzFfPFI%3D")
|
||||
.addHeader("Accept", "application/json")
|
||||
.build();
|
||||
|
||||
protected final HttpResponse getSecurityGroupResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/getsecuritygroupresponse.json"))
|
||||
.build();
|
||||
|
||||
protected final HttpRequest createSecurityGroup = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
.addQueryParam("command", "createSecurityGroup")
|
||||
.addQueryParam("name", "jclouds-test")
|
||||
.addQueryParam("apiKey", "APIKEY")
|
||||
.addQueryParam("signature", "BdgmqGsvjPmP4PxsEKEpq6buwuA%3D")
|
||||
.addHeader("Accept", "application/json")
|
||||
.build();
|
||||
|
||||
protected final HttpResponse createSecurityGroupResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/createsecuritygroupresponse.json"))
|
||||
.build();
|
||||
|
||||
protected final HttpRequest authorizeIngress = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
.addQueryParam("command", "authorizeSecurityGroupIngress")
|
||||
.addQueryParam("securitygroupid", "30")
|
||||
.addQueryParam("protocol", "TCP")
|
||||
.addQueryParam("startport", "22")
|
||||
.addQueryParam("endport", "22")
|
||||
.addQueryParam("cidrlist", "0.0.0.0/0")
|
||||
.addQueryParam("apiKey", "APIKEY")
|
||||
.addQueryParam("signature", "GVtXzAl/Q7z4wnvKEHtdV0lxv2o%3D")
|
||||
.addHeader("Accept", "application/json")
|
||||
.build();
|
||||
|
||||
protected final HttpResponse authorizeIngressResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/authorizesecuritygroupingressresponse.json"))
|
||||
.build();
|
||||
|
||||
protected final HttpRequest createSSHKeyPair = HttpRequest.builder().method("GET")
|
||||
.endpoint("http://localhost:8080/client/api")
|
||||
.addQueryParam("response", "json")
|
||||
.addQueryParam("command", "createSSHKeyPair")
|
||||
.addQueryParam("name", "jclouds-test")
|
||||
.addQueryParam("apiKey", "APIKEY")
|
||||
.addQueryParam("signature", "er6YjvUjPFwxy/x/aAVNW9Z8yo8%3D")
|
||||
.addHeader("Accept", "application/json")
|
||||
.build();
|
||||
|
||||
protected final HttpResponse createSSHKeyPairResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/createsshkeypairresponse-2.json"))
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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.cloudstack.predicates;
|
||||
|
||||
import static org.jclouds.cloudstack.predicates.SecurityGroupPredicates.portInRange;
|
||||
import static org.jclouds.cloudstack.predicates.SecurityGroupPredicates.hasCidr;
|
||||
import static org.jclouds.cloudstack.predicates.SecurityGroupPredicates.portInRangeForCidr;
|
||||
import static org.jclouds.cloudstack.predicates.SecurityGroupPredicates.nameEquals;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.cloudstack.domain.IngressRule;
|
||||
import org.jclouds.cloudstack.domain.SecurityGroup;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* @author Andrew Bayer
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class SecurityGroupPredicatesTest {
|
||||
|
||||
public SecurityGroup group() {
|
||||
return SecurityGroup
|
||||
.builder()
|
||||
.id("13")
|
||||
.name("default")
|
||||
.description("description")
|
||||
.account("adrian")
|
||||
.domainId("1")
|
||||
.domain("ROOT")
|
||||
.ingressRules(
|
||||
ImmutableSet.of(
|
||||
|
||||
IngressRule.builder().id("5").protocol("tcp").startPort(22).endPort(22)
|
||||
.securityGroupName("adriancole").account("adrian").build(),
|
||||
IngressRule.builder().id("6").protocol("udp").startPort(11).endPort(11).CIDR("1.1.1.1/24").build(),
|
||||
IngressRule.builder().id("7").protocol("tcp").startPort(40).endPort(50).CIDR("1.1.1.1/24").build(),
|
||||
IngressRule.builder().id("8").protocol("tcp").startPort(60).endPort(60).CIDR("2.2.2.2/16").build()
|
||||
)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPortInRange() {
|
||||
assertTrue(portInRange(22).apply(group()));
|
||||
assertTrue(portInRange(45).apply(group()));
|
||||
assertFalse(portInRange(100).apply(group()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasCidr() {
|
||||
assertTrue(hasCidr("1.1.1.1/24").apply(group()));
|
||||
assertFalse(hasCidr("3.3.3.3/25").apply(group()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPortInRangeForCidr() {
|
||||
assertTrue(portInRangeForCidr(11, "1.1.1.1/24").apply(group()));
|
||||
assertTrue(portInRangeForCidr(45, "1.1.1.1/24").apply(group()));
|
||||
assertFalse(portInRangeForCidr(45, "2.2.2.2/16").apply(group()));
|
||||
assertFalse(portInRangeForCidr(11, "2.2.2.2/16").apply(group()));
|
||||
assertFalse(portInRangeForCidr(11, "3.3.3.3/25").apply(group()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNameEquals() {
|
||||
assertTrue(nameEquals("default").apply(group()));
|
||||
assertFalse(nameEquals("not-default").apply(group()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
{ "authorizesecuritygroupingressresponse" :
|
||||
{"jobid":"13330fc9-8b3e-4582-aa3e-90883c041010"} }
|
|
@ -0,0 +1,3 @@
|
|||
{ "listsecuritygroupsresponse" : { "securitygroup" :
|
||||
{"id":30,"name":"jclouds-test","account":"adrian","domainid":1,"domain":"ROOT"}
|
||||
} }
|
|
@ -0,0 +1,4 @@
|
|||
{ "createsshkeypairresponse" : { "keypair" : {
|
||||
"name":"jclouds-test",
|
||||
"fingerprint":"1c:06:74:52:3b:99:1c:95:5c:04:c2:f4:ba:77:6e:7b",
|
||||
"privatekey":"-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDZo/EF4Ew1uEW0raz7vCs28lBwy0UKV2Xr606gaEgxO7h9mSXZ\n4x2K/KQ1NMnrbjppxGycLh9EKPWAO3ezFULAyuOZW4Fy+xRS8+3MAijxBJY/KBgl\nx5rJm2ILumRkTNkMlLGCSBb9SOqYRN1VpOy7kn3StzU9LdJ/snKVE2JLHQIDAQAB\nAoGBAMnL5okKRd9xcsBqYIAxIuiZmNhcwTErhEdRMOAukPGFbDSYsa3rldLvGdpz\njd2LoQG8rO/LHBZ429kASqZzyiV+NvcgH+tFNJSVAigjSICfhEKF9PY2TiAkrg7S\nGyJgAjpPWQc2sQh0dE8EPEtBiq4ibXfMTDmbs1d/vnfdwtQJAkEA+AX5Y+xgWj74\ndYETmNLyLhNZpftLizEfIYj7lCVhsbFwVb8jbM1m8n8bxwGjls1w/ico1CWcQna+\nUnAfA8kJvwJBAOCj0YgDKpYd0OLQhvI3212J9QcQpJEkDOTYiMwXNHCNMKRpoF47\nMPPX+GG8YzUiQAi9/OG4pDKCjzQWE/ebiiMCQQCssnQ5WICqtggIwYykr9VDseON\nSFIMpHJ5xkjumazRrqx6eDGxc8BH/6uWwRRoT7pqrVeniFyqhsX03u8pkpU/AkBj\nWfCcwBHArNUqy2EzlWKuvwogosq16oTNXbs60HR/5uIBhTnJE1K2NemDiGc0I77A\nXw6N4jS0piuhtLYGB8OTAkEA50abdbduXWcr62Z6E8G/6LNFaNg0uBuVgwSHtJMd\ndNeUtVDHQCHSf3tvxXTAtaB9PCnGOfgm/dyYWEMf3rMoHQ==\n-----END RSA PRIVATE KEY-----\n"} } }
|
|
@ -0,0 +1 @@
|
|||
{ "listsecuritygroupsresponse" : { count: 0, "securitygroup" : [ { } ] } }
|
|
@ -0,0 +1 @@
|
|||
{ "listzonesresponse" : { "zone" : [ {"id":2,"name":"Chicago","networktype":"Advanced","securitygroupsenabled":true} ] } }
|
|
@ -0,0 +1 @@
|
|||
{ "listnetworksresponse" : { "network" : [ {"id":204,"name":"Virtual Network","displaytext":"A dedicated virtualized network for your account. The broadcast domain is contained within a VLAN and all public network access is routed out by a virtual router.","broadcastdomaintype":"Vlan","traffictype":"Guest","zoneid":2,"networkofferingid":6,"networkofferingname":"DefaultVirtualizedNetworkOffering","networkofferingdisplaytext":"Virtual Vlan","networkofferingavailability":"Required","isshared":false,"issystem":false,"state":"Implemented","related":204,"broadcasturi":"vlan://240","dns1":"8.8.8.8","type":"Virtual","account":"adrian","domainid":1,"domain":"ROOT","isdefault":true,"service":[{"name":"Vpn","capability":[{"name":"SupportedVpnTypes","value":"pptp,l2tp,ipsec"}]},{"name":"Gateway"},{"name":"UserData"},{"name":"Dhcp"},{"name":"Firewall","capability":[{"name":"SupportedSourceNatTypes","value":"per account"},{"name":"StaticNat","value":"true"},{"name":"TrafficStatistics","value":"per public ip"},{"name":"PortForwarding","value":"true"},{"name":"MultipleIps","value":"true"},{"name":"SupportedProtocols","value":"tcp,udp"}]},{"name":"Dns"},{"name":"Lb","capability":[{"name":"SupportedLbAlgorithms","value":"roundrobin,leastconn,source"},{"name":"SupportedProtocols","value":"tcp, udp"}]}],"networkdomain":"cs3cloud.internal"} ] } }
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"queryasyncjobresultresponse": {
|
||||
"accountid": 3,
|
||||
"userid": 3,
|
||||
"cmd": "com.cloud.api.commands.AuthorizeSecurityGroupIngressCmd",
|
||||
"jobstatus": 1,
|
||||
"jobprocstatus": 0,
|
||||
"jobresultcode": 0,
|
||||
"jobresulttype": "object",
|
||||
"jobresult": {
|
||||
"securitygroup": {
|
||||
"id": "30",
|
||||
"name": "jclouds-test",
|
||||
"account": "adrian",
|
||||
"domainid": "1",
|
||||
"domain": "ROOT",
|
||||
"ingressrule": [
|
||||
{
|
||||
"ruleid": "35bb2ad4-e0cb-48a0-9534-fc4a067b5665",
|
||||
"protocol": "tcp",
|
||||
"startport": 22,
|
||||
"endport": 22,
|
||||
"cidr": "0.0.0.0/0"
|
||||
}
|
||||
],
|
||||
"egressrule": [
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
"jobid": "13330fc9-8b3e-4582-aa3e-90883c041010",
|
||||
"created": "2013-06-04T14:37:26-0700"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
{
|
||||
"queryasyncjobresultresponse": {
|
||||
"jobid": 50006,
|
||||
"accountid": 3,
|
||||
"userid": 3,
|
||||
"cmd": "com.cloud.api.commands.DeployVMCmd",
|
||||
"jobstatus": 1,
|
||||
"jobprocstatus": 0,
|
||||
"jobresultcode": 0,
|
||||
"jobresult": {
|
||||
"virtualmachine": {
|
||||
"id": 1234,
|
||||
"name": "i-3-218-VM",
|
||||
"displayname": "i-3-218-VM",
|
||||
"account": "adrian",
|
||||
"domainid": 1,
|
||||
"domain": "ROOT",
|
||||
"created": "2011-02-27T08:39:10-0800",
|
||||
"state": "Running",
|
||||
"haenable": false,
|
||||
"zoneid": 1,
|
||||
"zonename": "San Jose 1",
|
||||
"templateid": 203,
|
||||
"templatename": "Centos 5.3 Password Managed",
|
||||
"templatedisplaytext": "Centos 5.3 Password Managed",
|
||||
"passwordenabled": true,
|
||||
"serviceofferingid": 1,
|
||||
"serviceofferingname": "Small Instance",
|
||||
"cpunumber": 1,
|
||||
"cpuspeed": 500,
|
||||
"memory": 512,
|
||||
"guestosid": 12,
|
||||
"rootdeviceid": 0,
|
||||
"rootdevicetype": "NetworkFilesystem",
|
||||
"securitygroup": [
|
||||
{
|
||||
"id": "30",
|
||||
"name": "jclouds-test",
|
||||
"account": "adrian",
|
||||
"domainid": "1",
|
||||
"domain": "ROOT",
|
||||
"ingressrule": [
|
||||
{
|
||||
"ruleid": "35bb2ad4-e0cb-48a0-9534-fc4a067b5665",
|
||||
"protocol": "tcp",
|
||||
"startport": 22,
|
||||
"endport": 22,
|
||||
"cidr": "0.0.0.0/0"
|
||||
}
|
||||
],
|
||||
"egressrule": [
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"password": "dD7jwajkh",
|
||||
"nic": [{
|
||||
"id": 250,
|
||||
"networkid": 204,
|
||||
"netmask": "255.255.255.0",
|
||||
"gateway": "10.1.1.1",
|
||||
"ipaddress": "10.1.1.195",
|
||||
"traffictype": "Guest",
|
||||
"type": "Virtual",
|
||||
"isdefault": true
|
||||
}],
|
||||
"hypervisor": "XenServer"
|
||||
}
|
||||
},
|
||||
"created": "2011-02-27T08:39:10-0800"
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue