Repository: brooklyn-server Updated Branches: refs/heads/master 9ebc32c10 -> b1fc16d02
A default network is now created when deploying to Azure ARM Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/44470854 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/44470854 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/44470854 Branch: refs/heads/master Commit: 44470854f19224fd3c62fb912db7951af0b38c3f Parents: 0b9717a Author: graeme.miller <[email protected]> Authored: Tue Jun 20 15:59:04 2017 +0100 Committer: graeme.miller <[email protected]> Committed: Wed Jun 21 11:44:04 2017 +0100 ---------------------------------------------------------------------- .../location/jclouds/JcloudsLocation.java | 6 + .../creator/DefaultAzureArmNetworkCreator.java | 129 +++++++++++++ .../DefaultAzureArmNetworkCreatorTest.java | 181 +++++++++++++++++++ 3 files changed, 316 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/44470854/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java index a701083..2409727 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java @@ -80,6 +80,7 @@ import org.apache.brooklyn.core.mgmt.persist.PersistenceObjectStore; import org.apache.brooklyn.core.mgmt.persist.jclouds.JcloudsBlobStoreBasedObjectStore; import org.apache.brooklyn.location.jclouds.api.JcloudsLocationPublic; import org.apache.brooklyn.location.jclouds.networking.JcloudsPortForwarderExtension; +import org.apache.brooklyn.location.jclouds.networking.creator.DefaultAzureArmNetworkCreator; import org.apache.brooklyn.location.jclouds.templates.PortableTemplateBuilder; import org.apache.brooklyn.location.jclouds.templates.customize.TemplateBuilderCustomizer; import org.apache.brooklyn.location.jclouds.templates.customize.TemplateBuilderCustomizers; @@ -667,6 +668,11 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im Template template; try { + // Create default network for Azure ARM if necessary + if ("azurecompute-arm".equals(this.getProvider())) { + DefaultAzureArmNetworkCreator.createDefaultNetworkAndAddToTemplateOptions(computeService, setup); + } + // Setup the template template = buildTemplate(computeService, setup, ImmutableList.of(customizersDelegate)); boolean expectWindows = isWindows(template, setup); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/44470854/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/creator/DefaultAzureArmNetworkCreator.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/creator/DefaultAzureArmNetworkCreator.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/creator/DefaultAzureArmNetworkCreator.java new file mode 100644 index 0000000..f12d929 --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/creator/DefaultAzureArmNetworkCreator.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds.networking.creator; + +import static org.apache.brooklyn.core.location.cloud.CloudLocationConfig.CLOUD_REGION_ID; +import static org.apache.brooklyn.location.jclouds.api.JcloudsLocationConfigPublic.NETWORK_NAME; +import static org.apache.brooklyn.location.jclouds.api.JcloudsLocationConfigPublic.TEMPLATE_OPTIONS; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableMap; + +import org.jclouds.azurecompute.arm.AzureComputeApi; +import org.jclouds.azurecompute.arm.domain.ResourceGroup; +import org.jclouds.azurecompute.arm.domain.Subnet; +import org.jclouds.azurecompute.arm.domain.VirtualNetwork; +import org.jclouds.compute.ComputeService; + +import org.apache.brooklyn.util.core.config.ConfigBag; + +public class DefaultAzureArmNetworkCreator { + + public static final Logger LOG = LoggerFactory.getLogger(DefaultAzureArmNetworkCreator.class); + + private static final String DEFAULT_RESOURCE_GROUP = "brooklyn-default-resource-group"; + private static final String DEFAULT_NETWORK_NAME = "brooklyn-default-network"; + private static final String DEFAULT_SUBNET_NAME = "brooklyn-default-subnet"; + + private static final String defaultVnetAddressPrefix = "10.1.0.0/16"; + private static final String defaultSubnetAddressPrefix = "10.1.0.0/24"; + + public static void createDefaultNetworkAndAddToTemplateOptions(ComputeService computeService, ConfigBag config) { + Map<String, Object> templateOptions = config.get(TEMPLATE_OPTIONS); + + //Only create a default network if we haven't specified a network name (in template options or config) or ip options + if (config.containsKey(NETWORK_NAME)) { + LOG.info("Network config specified when creating Azure location. Not creating default network"); + return; + } + if (templateOptions != null && (templateOptions.containsKey(NETWORK_NAME.getName()) || templateOptions.containsKey("ipOptions"))) { + LOG.info("Network config specified when creating Azure location. Not creating default network"); + return; + } + + LOG.info("Network config not specified when creating Azure location. Creating default network if doesn't exist"); + + AzureComputeApi api = computeService.getContext().unwrapApi(AzureComputeApi.class); + String location = config.get(CLOUD_REGION_ID); + + String resourceGroupName = DEFAULT_RESOURCE_GROUP + "-" + location; + String vnetName = DEFAULT_NETWORK_NAME + "-" + location; + String subnetName = DEFAULT_SUBNET_NAME + "-" + location; + + //Check if default already exists + Subnet preexistingSubnet = api.getSubnetApi(resourceGroupName, vnetName).get(subnetName); + if(preexistingSubnet != null){ + LOG.info("Default Azure network and subnet already created, "+vnetName); + updateTemplateOptions(config, preexistingSubnet); + return; + } + + + LOG.info("Network config not specified when creating Azure location and default network/subnet does not exists. Creating"); + + createResourceGroupIfNeeded(api, resourceGroupName, location); + + //Setup properties for creating subnet/network + Subnet subnet = Subnet.create(subnetName, null, null, + Subnet.SubnetProperties.builder().addressPrefix(defaultSubnetAddressPrefix).build()); + + VirtualNetwork.VirtualNetworkProperties virtualNetworkProperties = VirtualNetwork.VirtualNetworkProperties + .builder().addressSpace(VirtualNetwork.AddressSpace.create(Arrays.asList(defaultVnetAddressPrefix))) + .subnets(Arrays.asList(subnet)).build(); + + //Create network + api.getVirtualNetworkApi(resourceGroupName).createOrUpdate(vnetName, location, virtualNetworkProperties); + Subnet createdSubnet = api.getSubnetApi(resourceGroupName, vnetName).get(subnetName); + + //Add config + updateTemplateOptions(config, createdSubnet); + + } + + private static void updateTemplateOptions(ConfigBag config, Subnet createdSubnet){ + Map<String, Object> templateOptions = config.get(TEMPLATE_OPTIONS); + + if(templateOptions == null) { + templateOptions = new HashMap<>(); + config.put(TEMPLATE_OPTIONS, templateOptions); + } + + Map<String, Object> ipOptions = new HashMap<>(); + ipOptions.put("allocateNewPublicIp", true); //JClouds will not provide a public IP unless we set this + ipOptions.put("subnet", createdSubnet.id()); + templateOptions.put("ipOptions", ipOptions); + } + + + private static void createResourceGroupIfNeeded(AzureComputeApi api, String resourceGroup, String location) { + LOG.debug("using resource group [%s]", resourceGroup); + ResourceGroup rg = api.getResourceGroupApi().get(resourceGroup); + if (rg == null) { + LOG.debug("resource group [%s] does not exist. Creating!", resourceGroup); + api.getResourceGroupApi().create(resourceGroup, location, + ImmutableMap.of("description", "brooklyn default resource group")); + } + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/44470854/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/creator/DefaultAzureArmNetworkCreatorTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/creator/DefaultAzureArmNetworkCreatorTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/creator/DefaultAzureArmNetworkCreatorTest.java new file mode 100644 index 0000000..2dd6a5d --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/creator/DefaultAzureArmNetworkCreatorTest.java @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds.networking.creator; + +import static org.apache.brooklyn.core.location.cloud.CloudLocationConfig.CLOUD_REGION_ID; +import static org.apache.brooklyn.location.jclouds.api.JcloudsLocationConfigPublic.NETWORK_NAME; +import static org.apache.brooklyn.location.jclouds.api.JcloudsLocationConfigPublic.TEMPLATE_OPTIONS; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import org.jclouds.azurecompute.arm.AzureComputeApi; +import org.jclouds.azurecompute.arm.domain.Subnet; +import org.jclouds.azurecompute.arm.features.ResourceGroupApi; +import org.jclouds.azurecompute.arm.features.SubnetApi; +import org.jclouds.azurecompute.arm.features.VirtualNetworkApi; +import org.jclouds.compute.ComputeService; + +import org.apache.brooklyn.util.core.config.ConfigBag; + +public class DefaultAzureArmNetworkCreatorTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) ComputeService computeService; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) AzureComputeApi azureComputeApi; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) ResourceGroupApi resourceGroupApi; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) VirtualNetworkApi virtualNetworkApi; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) SubnetApi subnetApi; + + @Mock Subnet subnet; + + final String TEST_RESOURCE_GROUP = "brooklyn-default-resource-group-test-loc"; + final String TEST_NETWORK_NAME = "brooklyn-default-network-test-loc"; + final String TEST_SUBNET_NAME = "brooklyn-default-subnet-test-loc"; + final String TEST_SUBNET_ID = "/test/resource/id"; + final String TEST_LOCATION = "test-loc"; + + @BeforeMethod + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testPreExisting() { + //Setup config bag + ConfigBag configBag = ConfigBag.newInstance(); + configBag.put(CLOUD_REGION_ID, TEST_LOCATION); + + //Setup mocks + when(computeService.getContext().unwrapApi(AzureComputeApi.class)).thenReturn(azureComputeApi); + when(azureComputeApi.getSubnetApi(TEST_RESOURCE_GROUP, TEST_NETWORK_NAME)).thenReturn(subnetApi); + when(subnetApi.get(TEST_SUBNET_NAME)).thenReturn(subnet); + when(subnet.id()).thenReturn(TEST_SUBNET_ID); + + //Test + DefaultAzureArmNetworkCreator.createDefaultNetworkAndAddToTemplateOptions(computeService, configBag); + + //verify + verify(subnetApi).get(TEST_SUBNET_NAME); + verify(subnet).id(); + verify(azureComputeApi).getSubnetApi(TEST_RESOURCE_GROUP, TEST_NETWORK_NAME); + + Map<String, Object> templateOptions = configBag.get(TEMPLATE_OPTIONS); + Map<String, Object> ipOptions = (Map<String, Object>)templateOptions.get("ipOptions"); + assertEquals(ipOptions.get("subnet"), TEST_SUBNET_ID); + assertEquals(ipOptions.get("allocateNewPublicIp"), true); + } + + @Test + public void testVanilla() { + //Setup config bag + ConfigBag configBag = ConfigBag.newInstance(); + configBag.put(CLOUD_REGION_ID, TEST_LOCATION); + + //Setup mocks + when(computeService.getContext().unwrapApi(AzureComputeApi.class)).thenReturn(azureComputeApi); + when(azureComputeApi.getSubnetApi(TEST_RESOURCE_GROUP, TEST_NETWORK_NAME)).thenReturn(subnetApi); + when(subnetApi.get(TEST_SUBNET_NAME)).thenReturn(null).thenReturn(subnet); //null first time, subnet next + when(subnet.id()).thenReturn(TEST_SUBNET_ID); + + when(azureComputeApi.getResourceGroupApi()).thenReturn(resourceGroupApi); + when(resourceGroupApi.get(TEST_RESOURCE_GROUP)).thenReturn(null); + + when(azureComputeApi.getVirtualNetworkApi(TEST_RESOURCE_GROUP)).thenReturn(virtualNetworkApi); + + + //Test + DefaultAzureArmNetworkCreator.createDefaultNetworkAndAddToTemplateOptions(computeService, configBag); + + //verify + verify(subnetApi, times(2)).get(TEST_SUBNET_NAME); + verify(subnet).id(); + verify(azureComputeApi, times(2)).getSubnetApi(TEST_RESOURCE_GROUP, TEST_NETWORK_NAME); + + verify(azureComputeApi, times(2)).getResourceGroupApi(); + verify(resourceGroupApi).get(TEST_RESOURCE_GROUP); + verify(azureComputeApi).getVirtualNetworkApi(TEST_RESOURCE_GROUP); + + Map<String, Object> templateOptions = configBag.get(TEMPLATE_OPTIONS); + Map<String, Object> ipOptions = (Map<String, Object>)templateOptions.get("ipOptions"); + assertEquals(ipOptions.get("subnet"), TEST_SUBNET_ID); + assertEquals(ipOptions.get("allocateNewPublicIp"), true); + } + + @Test + public void testNetworkInConfig() { + ConfigBag configBag = ConfigBag.newInstance(); + configBag.put(CLOUD_REGION_ID, TEST_LOCATION); + configBag.put(NETWORK_NAME, TEST_NETWORK_NAME); + + Map<String, Object> configCopy = configBag.getAllConfig(); + + DefaultAzureArmNetworkCreator.createDefaultNetworkAndAddToTemplateOptions(computeService, configBag); + + //Ensure nothing changed, and no calls were made to the compute service + assertEquals(configCopy, configBag.getAllConfig()); + Mockito.verifyZeroInteractions(computeService); + } + + @Test + public void testNetworkInTemplate() { + HashMap<String, Object> templateOptions = new HashMap<>(); + templateOptions.put(NETWORK_NAME.getName(), TEST_NETWORK_NAME); + + ConfigBag configBag = ConfigBag.newInstance(); + configBag.put(CLOUD_REGION_ID, TEST_LOCATION); + configBag.put(TEMPLATE_OPTIONS, templateOptions); + + Map<String, Object> configCopy = configBag.getAllConfig(); + + DefaultAzureArmNetworkCreator.createDefaultNetworkAndAddToTemplateOptions(computeService, configBag); + + //Ensure nothing changed, and no calls were made to the compute service + assertEquals(configCopy, configBag.getAllConfig()); + Mockito.verifyZeroInteractions(computeService); + } + + @Test + public void testIpOptionsInTemplate() { + HashMap<String, Object> templateOptions = new HashMap<>(); + templateOptions.put("ipOptions", TEST_NETWORK_NAME); + + ConfigBag configBag = ConfigBag.newInstance(); + configBag.put(CLOUD_REGION_ID, TEST_LOCATION); + configBag.put(TEMPLATE_OPTIONS, templateOptions); + + Map<String, Object> configCopy = configBag.getAllConfig(); + + DefaultAzureArmNetworkCreator.createDefaultNetworkAndAddToTemplateOptions(computeService, configBag); + + //Ensure nothing changed, and no calls were made to the compute service + assertEquals(configCopy, configBag.getAllConfig()); + Mockito.verifyZeroInteractions(computeService); + } +}
