You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ab...@apache.org on 2013/06/06 19:26:36 UTC
[1/3] git commit: JCLOUDS-105. Real fix for keypairs being ignored if
VM says it's password-enabled.
Updated Branches:
refs/heads/master 7651657cf -> a3488dc43
JCLOUDS-105. Real fix for keypairs being ignored if VM says it's password-enabled.
Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/commit/a3488dc4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/tree/a3488dc4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/diff/a3488dc4
Branch: refs/heads/master
Commit: a3488dc43cd60580374532f3780bdb1b1ec8697c
Parents: fbe637c
Author: Andrew Bayer <an...@gmail.com>
Authored: Fri May 31 10:53:18 2013 -0700
Committer: Andrew Bayer <an...@gmail.com>
Committed: Thu Jun 6 10:26:20 2013 -0700
----------------------------------------------------------------------
.../strategy/CloudStackComputeServiceAdapter.java | 23 ++++--
.../CloudStackComputeServiceAdapterExpectTest.java | 63 ++++++++++++-
.../CloudStackComputeServiceAdapterLiveTest.java | 71 ++++-----------
3 files changed, 91 insertions(+), 66 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a3488dc4/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
index a4f7176..5ad2fff 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
@@ -174,12 +174,19 @@ public class CloudStackComputeServiceAdapter implements
}
if (templateOptions.getKeyPair() != null) {
+ SshKeyPair keyPair = null;
if (templateOptions.getLoginPrivateKey() != null) {
String pem = templateOptions.getLoginPrivateKey();
- SshKeyPair keyPair = SshKeyPair.builder().name(templateOptions.getKeyPair())
+ keyPair = SshKeyPair.builder().name(templateOptions.getKeyPair())
.fingerprint(fingerprintPrivateKey(pem)).privateKey(pem).build();
keyPairCache.asMap().put(keyPair.getName(), keyPair);
options.keyPair(keyPair.getName());
+ } else if (client.getSSHKeyPairClient().getSSHKeyPair(templateOptions.getKeyPair()) != null) {
+ keyPair = client.getSSHKeyPairClient().getSSHKeyPair(templateOptions.getKeyPair());
+ }
+ if (keyPair != null) {
+ keyPairCache.asMap().put(keyPair.getName(), keyPair);
+ options.keyPair(keyPair.getName());
}
} else if (templateOptions.shouldGenerateKeyPair()) {
SshKeyPair keyPair = keyPairCache.getUnchecked(namingConvention.create()
@@ -214,13 +221,15 @@ public class CloudStackComputeServiceAdapter implements
templateId, options);
VirtualMachine vm = blockUntilJobCompletesAndReturnResult.<VirtualMachine>apply(job);
logger.debug("--- virtualmachine: %s", vm);
- LoginCredentials credentials = null;
- if (vm.isPasswordEnabled()) {
- assert vm.getPassword() != null : vm;
- credentials = LoginCredentials.builder().password(vm.getPassword()).build();
+ LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder();
+ if (!vm.isPasswordEnabled() || templateOptions.getKeyPair() != null) {
+ SshKeyPair keyPair = keyPairCache.getUnchecked(templateOptions.getKeyPair());
+ credentialsBuilder.privateKey(keyPair.getPrivateKey());
} else {
- credentials = LoginCredentials.fromCredentials(credentialStore.get("keypair#" + templateOptions.getKeyPair()));
+ assert vm.getPassword() != null : vm;
+ credentialsBuilder.password(vm.getPassword());
}
+
if (templateOptions.shouldSetupStaticNat()) {
Capabilities capabilities = client.getConfigurationClient().listCapabilities();
// TODO: possibly not all network ids, do we want to do this
@@ -241,7 +250,7 @@ public class CloudStackComputeServiceAdapter implements
}
}
}
- return new NodeAndInitialCredentials<VirtualMachine>(vm, vm.getId() + "", credentials);
+ return new NodeAndInitialCredentials<VirtualMachine>(vm, vm.getId() + "", credentialsBuilder.build());
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a3488dc4/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java
index 1e6ff6f..4eb89aa 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java
@@ -83,6 +83,57 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
.payload(payloadFromResource("/queryasyncjobresultresponse-authorizeingress.json"))
.build();
+ HttpRequest listCapabilitiesNotListAll = HttpRequest.builder().method("GET")
+ .endpoint("http://localhost:8080/client/api")
+ .addQueryParam("response", "json")
+ .addQueryParam("command", "listCapabilities")
+ .addQueryParam("apiKey", "APIKEY")
+ .addQueryParam("signature", "l3PVoJnKK2G2gHk3HPHtpwWjlW4%3D")
+ .addHeader("Accept", "application/json")
+ .build();
+
+ public void testCreateNodeWithGroupEncodedIntoName() {
+ 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("apiKey", "APIKEY")
+ .addQueryParam("signature", "wJ%2BiflOS3am5qcjQOd8Y/Pw8/Dc%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(queryAsyncJobResult, queryAsyncJobResultResponse)
+ .build();
+
+ Injector forNode = requestsSendResponses(requestResponseMap);
+
+ Template template = forNode.getInstance(TemplateBuilder.class).osFamily(OsFamily.CENTOS).build();
+ template.getOptions().as(CloudStackTemplateOptions.class).setupStaticNat(false);
+
+ CloudStackComputeServiceAdapter adapter = forNode.getInstance(CloudStackComputeServiceAdapter.class);
+
+ NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
+ template);
+ assertNotNull(server);
+ assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
+ }
+
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPair() throws IOException {
HttpRequest deployVM = HttpRequest.builder().method("GET")
.endpoint("http://localhost:8080/client/api")
@@ -126,7 +177,7 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
template);
assertNotNull(server);
- assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
+ assertEquals(server.getCredentials().getPrivateKey(), privKey);
}
public void testCreateNodeWithGroupEncodedIntoNameWithGenerateKeyPair() throws IOException {
@@ -171,7 +222,7 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
template);
assertNotNull(server);
- assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
+ assertNotNull(server.getCredentials().getPrivateKey());
}
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairDefaultSecurityGroup() throws IOException {
@@ -220,7 +271,7 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
template);
assertNotNull(server);
- assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
+ assertEquals(server.getCredentials().getPrivateKey(), privKey);
}
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairGenerateSecurityGroup() throws IOException {
@@ -271,11 +322,12 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
.overrideLoginPrivateKey(privKey);
CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);
+ CloudStackContext context = forKeyPair.getInstance(CloudStackContext.class);
NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
template);
assertNotNull(server);
- assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
+ assertEquals(server.getCredentials().getPrivateKey(), privKey);
}
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairAssignedToAccountAndDomain() throws IOException {
@@ -321,11 +373,12 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
.overrideLoginPrivateKey(privKey);
CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);
+ CloudStackContext context = forKeyPair.getInstance(CloudStackContext.class);
NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
template);
assertNotNull(server);
- assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
+ assertEquals(server.getCredentials().getPrivateKey(), privKey);
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a3488dc4/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java
index a1ad652..82efa60 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java
@@ -31,17 +31,27 @@ import javax.inject.Singleton;
import org.jclouds.cloudstack.CloudStackClient;
import org.jclouds.cloudstack.compute.config.CloudStackComputeServiceContextModule;
+import org.jclouds.cloudstack.compute.functions.OrphanedGroupsByZoneId;
+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.CloudStackComputeServiceAdapter;
import org.jclouds.cloudstack.compute.strategy.OptionsConverter;
+import org.jclouds.cloudstack.config.CloudStackParserModule;
+import org.jclouds.cloudstack.config.CloudStackRestClientModule;
import org.jclouds.cloudstack.domain.FirewallRule;
import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.NetworkType;
+import org.jclouds.cloudstack.domain.SecurityGroup;
import org.jclouds.cloudstack.domain.ServiceOffering;
+import org.jclouds.cloudstack.domain.SshKeyPair;
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.functions.CreateSecurityGroupIfNeeded;
import org.jclouds.cloudstack.functions.GetFirewallRulesByVirtualMachine;
import org.jclouds.cloudstack.functions.GetIPForwardingRulesByVirtualMachine;
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
@@ -55,15 +65,19 @@ import org.jclouds.cloudstack.suppliers.ZoneIdToZoneSupplier;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
import org.jclouds.compute.ComputeTestUtils;
+import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
+import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.functions.DefaultCredentialsFromImageOrOverridingCredentials;
import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
import org.jclouds.domain.Credentials;
+import org.jclouds.location.Provider;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
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;
@@ -72,6 +86,7 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
import com.google.common.net.HostAndPort;
import com.google.common.net.InetAddresses;
import com.google.inject.AbstractModule;
@@ -96,60 +111,8 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien
@BeforeGroups(groups = { "live" })
public void setupContext() {
super.setupContext();
- Module module = new AbstractModule() {
-
- @Override
- protected void configure() {
- bindProperties(binder(), setupProperties());
- bind(new TypeLiteral<Supplier<User>>() {
- }).annotatedWith(Memoized.class).to(GetCurrentUser.class).in(Scopes.SINGLETON);
- bind(new TypeLiteral<Supplier<Map<String, Network>>>() {
- }).annotatedWith(Memoized.class).to(NetworksForCurrentUser.class).in(Scopes.SINGLETON);
- bind(new TypeLiteral<Map<String, Credentials>>() {
- }).toInstance(credentialStore);
- bind(CloudStackClient.class).toInstance(client);
- bind(new TypeLiteral<Map<NetworkType, ? extends OptionsConverter>>() {}).
- toInstance(new CloudStackComputeServiceContextModule().optionsConverters());
- bind(String.class).annotatedWith(Names.named(PROPERTY_SESSION_INTERVAL)).toInstance("60");
- bind(new TypeLiteral<CacheLoader<String, Set<IPForwardingRule>>>() {
- }).to(GetIPForwardingRulesByVirtualMachine.class);
- bind(new TypeLiteral<CacheLoader<String, Set<FirewallRule>>>() {
- }).to(GetFirewallRulesByVirtualMachine.class);
- bind(new TypeLiteral<CacheLoader<String, Zone>>() {}).
- to(ZoneIdToZone.class);
- bind(new TypeLiteral<Supplier<LoadingCache<String, Zone>>>() {}).
- to(ZoneIdToZoneSupplier.class);
- install(new FactoryModuleBuilder().build(StaticNATVirtualMachineInNetwork.Factory.class));
- }
-
- @Provides
- @Singleton
- Supplier<Credentials> supplyCredentials(){
- return Suppliers.ofInstance(new Credentials(identity, credential));
- }
-
- @Provides
- @Singleton
- protected Predicate<String> jobComplete(JobComplete jobComplete) {
- return retry(jobComplete, 1200, 1, 5, SECONDS);
- }
-
- @Provides
- @Singleton
- protected LoadingCache<String, Set<IPForwardingRule>> getIPForwardingRulesByVirtualMachine(
- GetIPForwardingRulesByVirtualMachine getIPForwardingRules) {
- return CacheBuilder.newBuilder().build(getIPForwardingRules);
- }
-
-
- @Provides
- @Singleton
- protected LoadingCache<String, Set<FirewallRule>> getFirewallRulesByVirtualMachine(
- GetFirewallRulesByVirtualMachine getFirewallRules) {
- return CacheBuilder.newBuilder().build(getFirewallRules);
- }
- };
- adapter = Guice.createInjector(module, new SLF4JLoggingModule()).getInstance(
+
+ adapter = context.utils().injector().getInstance(
CloudStackComputeServiceAdapter.class);
keyPairName = prefix + "-adapter-test-keypair";
[2/3] JCLOUDS-106. Add proper support for generation/detection of
keypair/security groups to CloudStack.
Posted by ab...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPairTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPairTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPairTest.java
new file mode 100644
index 0000000..1bb09e6
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPairTest.java
@@ -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);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreateTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreateTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreateTest.java
new file mode 100644
index 0000000..fa6b749
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreateTest.java
@@ -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);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptionsTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptionsTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptionsTest.java
index 33a66fc..7bb81f5 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptionsTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptionsTest.java
@@ -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;
@@ -88,6 +90,27 @@ public class CloudStackTemplateOptionsTest {
}
@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();
assertEquals(options.as(CloudStackTemplateOptions.class).getNetworkIds(), ImmutableSet.of());
@@ -171,6 +194,27 @@ public class CloudStackTemplateOptionsTest {
}
@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");
assertEquals(options.as(CloudStackTemplateOptions.class).getKeyPair(), "test");
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClientTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClientTest.java
index 516d8f1..4240530 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClientTest.java
@@ -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"));
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
index f2d9d3f..f5cb8ed 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
@@ -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;
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeededTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeededTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeededTest.java
new file mode 100644
index 0000000..e131911
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeededTest.java
@@ -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);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackComputeServiceContextExpectTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackComputeServiceContextExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackComputeServiceContextExpectTest.java
index 9d24257..8b8d01d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackComputeServiceContextExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackComputeServiceContextExpectTest.java
@@ -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();
}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/SecurityGroupPredicatesTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/SecurityGroupPredicatesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/SecurityGroupPredicatesTest.java
new file mode 100644
index 0000000..16e473a
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/SecurityGroupPredicatesTest.java
@@ -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()));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/resources/authorizesecuritygroupingressresponse.json
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/resources/authorizesecuritygroupingressresponse.json b/apis/cloudstack/src/test/resources/authorizesecuritygroupingressresponse.json
new file mode 100644
index 0000000..e0280e4
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/authorizesecuritygroupingressresponse.json
@@ -0,0 +1,2 @@
+ { "authorizesecuritygroupingressresponse" :
+ {"jobid":"13330fc9-8b3e-4582-aa3e-90883c041010"} }
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/resources/createsecuritygroupresponse.json
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/resources/createsecuritygroupresponse.json b/apis/cloudstack/src/test/resources/createsecuritygroupresponse.json
new file mode 100644
index 0000000..99a2ad2
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/createsecuritygroupresponse.json
@@ -0,0 +1,3 @@
+{ "listsecuritygroupsresponse" : { "securitygroup" :
+{"id":30,"name":"jclouds-test","account":"adrian","domainid":1,"domain":"ROOT"}
+ } }
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/resources/createsshkeypairresponse-2.json
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/resources/createsshkeypairresponse-2.json b/apis/cloudstack/src/test/resources/createsshkeypairresponse-2.json
new file mode 100644
index 0000000..3dfb3b4
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/createsshkeypairresponse-2.json
@@ -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"} } }
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/resources/getsecuritygroupresponse.json
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/resources/getsecuritygroupresponse.json b/apis/cloudstack/src/test/resources/getsecuritygroupresponse.json
new file mode 100644
index 0000000..27743ed
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/getsecuritygroupresponse.json
@@ -0,0 +1 @@
+{ "listsecuritygroupsresponse" : { count: 0, "securitygroup" : [ { } ] } }
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/resources/getzoneresponse-2.json
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/resources/getzoneresponse-2.json b/apis/cloudstack/src/test/resources/getzoneresponse-2.json
new file mode 100644
index 0000000..7261716
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/getzoneresponse-2.json
@@ -0,0 +1 @@
+{ "listzonesresponse" : { "zone" : [ {"id":2,"name":"Chicago","networktype":"Advanced","securitygroupsenabled":true} ] } }
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/resources/listnetworksresponse-2.json
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/resources/listnetworksresponse-2.json b/apis/cloudstack/src/test/resources/listnetworksresponse-2.json
new file mode 100644
index 0000000..2f10824
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/listnetworksresponse-2.json
@@ -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"} ] } }
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-authorizeingress.json
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-authorizeingress.json b/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-authorizeingress.json
new file mode 100644
index 0000000..7ee489d
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-authorizeingress.json
@@ -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"
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-virtualmachine-securitygroup.json
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-virtualmachine-securitygroup.json b/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-virtualmachine-securitygroup.json
new file mode 100644
index 0000000..cc75e11
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/queryasyncjobresultresponse-virtualmachine-securitygroup.json
@@ -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"
+ }
+
+}
[3/3] git commit: JCLOUDS-106. Add proper support for
generation/detection of keypair/security groups to CloudStack.
Posted by ab...@apache.org.
JCLOUDS-106. Add proper support for generation/detection of keypair/security groups to CloudStack.
Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/commit/fbe637c8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/tree/fbe637c8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/diff/fbe637c8
Branch: refs/heads/master
Commit: fbe637c8bfb951532a48b6f70eff0dc739654ee9
Parents: 7651657
Author: Andrew Bayer <an...@gmail.com>
Authored: Sun Jun 2 12:23:19 2013 -0700
Committer: Andrew Bayer <an...@gmail.com>
Committed: Thu Jun 6 10:26:20 2013 -0700
----------------------------------------------------------------------
.../jclouds/cloudstack/CloudStackApiMetadata.java | 2 +
.../compute/CloudStackComputeService.java | 185 ++++++++++
.../CloudStackComputeServiceContextModule.java | 48 +++
.../compute/functions/OrphanedGroupsByZoneId.java | 77 +++++
.../compute/loaders/CreateUniqueKeyPair.java | 69 ++++
.../compute/loaders/FindSecurityGroupOrCreate.java | 83 +++++
.../compute/options/CloudStackTemplateOptions.java | 50 +++-
.../predicates/AllNodesInGroupTerminated.java | 56 +++
.../strategy/CloudStackComputeServiceAdapter.java | 59 +++-
.../cloudstack/config/CloudStackProperties.java | 5 +
.../org/jclouds/cloudstack/domain/ZoneAndName.java | 97 ++++++
.../domain/ZoneSecurityGroupNamePortsCidrs.java | 163 +++++++++
.../features/SecurityGroupAsyncClient.java | 12 +
.../cloudstack/features/SecurityGroupClient.java | 9 +
.../functions/CreateSecurityGroupIfNeeded.java | 124 +++++++
.../predicates/SecurityGroupPredicates.java | 161 +++++++++
.../predicates/SshKeyPairPredicates.java | 78 +++++
.../CloudStackComputeServiceAdapterExpectTest.java | 185 ++++++++++-
.../compute/loaders/CreateUniqueKeyPairTest.java | 73 ++++
.../loaders/FindSecurityGroupOrCreateTest.java | 261 +++++++++++++++
.../options/CloudStackTemplateOptionsTest.java | 44 +++
.../features/SecurityGroupAsyncClientTest.java | 18 +
.../features/SecurityGroupClientLiveTest.java | 4 +-
.../functions/CreateSecurityGroupIfNeededTest.java | 237 +++++++++++++
...eCloudStackComputeServiceContextExpectTest.java | 79 +++++
.../predicates/SecurityGroupPredicatesTest.java | 90 +++++
.../authorizesecuritygroupingressresponse.json | 2 +
.../resources/createsecuritygroupresponse.json | 3 +
.../test/resources/createsshkeypairresponse-2.json | 4 +
.../test/resources/getsecuritygroupresponse.json | 1 +
.../src/test/resources/getzoneresponse-2.json | 1 +
.../src/test/resources/listnetworksresponse-2.json | 1 +
...eryasyncjobresultresponse-authorizeingress.json | 34 ++
...esultresponse-virtualmachine-securitygroup.json | 73 ++++
34 files changed, 2369 insertions(+), 19 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackApiMetadata.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackApiMetadata.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackApiMetadata.java
index 652a6f0..53b9ed2 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackApiMetadata.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackApiMetadata.java
@@ -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;
}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java
new file mode 100644
index 0000000..bb17cac
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/CloudStackComputeService.java
@@ -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());
+ }
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
index 2eb613e..b4924be 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
@@ -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
@@ -187,6 +221,20 @@ public class CloudStackComputeServiceContextModule extends
@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(
CacheLoader<String, Set<IPForwardingRule>> getIPForwardingRules) {
return CacheBuilder.newBuilder().build(getIPForwardingRules);
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/OrphanedGroupsByZoneId.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/OrphanedGroupsByZoneId.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/OrphanedGroupsByZoneId.java
new file mode 100644
index 0000000..50dbe53
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/OrphanedGroupsByZoneId.java
@@ -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;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java
new file mode 100644
index 0000000..0ae2b05
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java
@@ -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;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java
new file mode 100644
index 0000000..6689371
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java
@@ -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()";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java
index 7a70b5f..4b05a21 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java
@@ -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);
@@ -103,6 +107,21 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
}
/**
+ * @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
*/
public CloudStackTemplateOptions networkId(String networkId) {
@@ -168,6 +187,21 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
}
/**
+ * @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)
*/
public CloudStackTemplateOptions account(String account) {
@@ -213,6 +247,13 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
}
/**
+ * @see CloudStackTemplateOptions#shouldGenerateSecurityGroup()
+ */
+ public static CloudStackTemplateOptions generateSecurityGroup(boolean enable) {
+ return new CloudStackTemplateOptions().generateSecurityGroup(enable);
+ }
+
+ /**
* @see CloudStackTemplateOptions#networkId
*/
public static CloudStackTemplateOptions networkId(String id) {
@@ -261,6 +302,13 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
}
/**
+ * @see CloudStackTemplateOptions#shouldGenerateKeyPair()
+ */
+ public static CloudStackTemplateOptions generateKeyPair(boolean enable) {
+ return new CloudStackTemplateOptions().generateKeyPair(enable);
+ }
+
+ /**
* @see CloudStackTemplateOptions#account
*/
public static CloudStackTemplateOptions account(String account) {
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java
new file mode 100644
index 0000000..d38a990
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java
@@ -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));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
index e839d30..a4f7176 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
@@ -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();
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java
index 34bc8ec..ac6e034 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java
@@ -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";
}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ZoneAndName.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ZoneAndName.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ZoneAndName.java
new file mode 100644
index 0000000..43c6ae04
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ZoneAndName.java
@@ -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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ZoneSecurityGroupNamePortsCidrs.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ZoneSecurityGroupNamePortsCidrs.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ZoneSecurityGroupNamePortsCidrs.java
new file mode 100644
index 0000000..415b901
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ZoneSecurityGroupNamePortsCidrs.java
@@ -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();
+ }
+}
+
+
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClient.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClient.java
index 31f862b..283ff5b 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClient.java
@@ -79,6 +79,18 @@ public interface SecurityGroupAsyncClient {
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
*/
@Named("createSecurityGroup")
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupClient.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupClient.java
index f3cb39e..ddb9422 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SecurityGroupClient.java
@@ -132,6 +132,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
*
* @param name
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeeded.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeeded.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeeded.java
new file mode 100644
index 0000000..b6a2a98
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/CreateSecurityGroupIfNeeded.java
@@ -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);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/SecurityGroupPredicates.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/SecurityGroupPredicates.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/SecurityGroupPredicates.java
new file mode 100644
index 0000000..bef1d29
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/SecurityGroupPredicates.java
@@ -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 + ")";
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/SshKeyPairPredicates.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/SshKeyPairPredicates.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/SshKeyPairPredicates.java
new file mode 100644
index 0000000..3037eb1
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/SshKeyPairPredicates.java
@@ -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 + ")";
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/fbe637c8/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java
index bab5459..1e6ff6f 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterExpectTest.java
@@ -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,160 @@ 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);
+
+ NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
+ template);
+ assertNotNull(server);
+ assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
+ }
+
+ 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);
@@ -106,7 +278,7 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
}
- public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairAssignedToAccountAndDomain() {
+ 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);