/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
package com.microsoft.azure.management.compute;
import com.microsoft.azure.management.network.Network;
import com.microsoft.azure.management.network.NetworkInterface;
import com.microsoft.azure.management.network.PublicIPAddress;
import com.microsoft.azure.management.resources.ResourceGroup;
import com.microsoft.azure.management.resources.fluentcore.arm.Region;
import com.microsoft.azure.management.resources.fluentcore.arm.models.Resource;
import com.microsoft.azure.management.resources.fluentcore.model.Creatable;
import com.microsoft.azure.management.resources.fluentcore.model.CreatedResources;
import com.microsoft.azure.management.resources.fluentcore.model.Indexable;
import com.microsoft.azure.management.resources.fluentcore.utils.SdkContext;
import com.microsoft.azure.management.storage.StorageAccount;
import com.microsoft.rest.RestClient;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import rx.Completable;
import rx.functions.Func1;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
public class VirtualMachineOperationsTests extends ComputeManagementTest {
private static String RG_NAME = "";
private static final Region REGION = Region.US_SOUTH_CENTRAL;
private static final String VMNAME = "javavm";
@Override
protected void initializeClients(RestClient restClient, String defaultSubscription, String domain) {
RG_NAME = generateRandomResourceName("javacsmrg", 15);
super.initializeClients(restClient, defaultSubscription, domain);
}
@Override
protected void cleanUpResources() {
resourceManager.resourceGroups().deleteByName(RG_NAME);
}
@Test
@Ignore("Can't be played from recording for some reason...")
public void canDeleteRelatedResourcesFromFailedParallelVMCreations() {
final int desiredVMCount = 15;
final Region region = Region.US_EAST;
final String resourceGroupName = RG_NAME;
// Create one resource group for everything, to ensure no reliance on resource groups
ResourceGroup resourceGroup = resourceManager.resourceGroups().define(resourceGroupName).withRegion(region).create();
// Needed for tracking related resources
final Map<String, Collection<Creatable<? extends Resource>>> vmNonNicResourceDefinitions = new HashMap<>();
final Map<String, Creatable<NetworkInterface>> nicDefinitions = new HashMap<>(); // Tracking NICs separately because they have to be deleted first
final Map<String, Creatable<VirtualMachine>> vmDefinitions = new HashMap<>();
final Map<String, String> createdResourceIds = new HashMap<>();
// Prepare a number of VM definitions along with their related resource definitions
for (int i = 0; i < desiredVMCount; i++) {
Collection<Creatable<? extends Resource>> relatedDefinitions = new ArrayList<>();
// Define a network for each VM
String networkName = SdkContext.randomResourceName("net", 14);
Creatable<Network> networkDefinition = networkManager.networks().define(networkName)
.withRegion(region)
.withExistingResourceGroup(resourceGroup)
.withAddressSpace("10.0." + i + ".0/29");
relatedDefinitions.add(networkDefinition);
// Define a PIP for each VM
String pipName = SdkContext.randomResourceName("pip", 14);
PublicIPAddress.DefinitionStages.WithCreate pipDefinition = this.networkManager.publicIPAddresses().define(pipName)
.withRegion(region)
.withExistingResourceGroup(resourceGroup);
relatedDefinitions.add(pipDefinition);
// Define a NIC for each VM
String nicName = SdkContext.randomResourceName("nic", 14);
Creatable<NetworkInterface> nicDefinition = networkManager.networkInterfaces().define(nicName)
.withRegion(region)
.withExistingResourceGroup(resourceGroup)
.withNewPrimaryNetwork(networkDefinition)
.withPrimaryPrivateIPAddressDynamic()
.withNewPrimaryPublicIPAddress(pipDefinition);
// Define a storage account for each VM
String storageAccountName = SdkContext.randomResourceName("st", 14);
Creatable<StorageAccount> storageAccountDefinition = storageManager.storageAccounts().define(storageAccountName)
.withRegion(region)
.withExistingResourceGroup(resourceGroup);
relatedDefinitions.add(storageAccountDefinition);
// Define an availability set for each VM
String availabilitySetName = SdkContext.randomResourceName("as", 14);
Creatable<AvailabilitySet> availabilitySetDefinition = computeManager.availabilitySets().define(availabilitySetName)
.withRegion(region)
.withExistingResourceGroup(resourceGroup);
relatedDefinitions.add(availabilitySetDefinition);
String vmName = SdkContext.randomResourceName("vm", 14);
// Define a VM
String userName;
if (i == 7) {
// Intentionally cause a failure in one of the VMs
userName = "";
} else {
userName = "tester";
}
Creatable<VirtualMachine> vmDefinition = computeManager.virtualMachines().define(vmName)
.withRegion(region)
.withExistingResourceGroup(resourceGroup)
.withNewPrimaryNetworkInterface(nicDefinition)
.withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_16_04_LTS)
.withRootUsername(userName)
.withRootPassword("Abcdef.123456!")
.withNewStorageAccount(storageAccountDefinition)
.withSize(VirtualMachineSizeTypes.BASIC_A1)
.withNewAvailabilitySet(availabilitySetDefinition);
// Keep track of all the related resource definitions based on the VM definition
vmNonNicResourceDefinitions.put(vmDefinition.key(), relatedDefinitions);
nicDefinitions.put(vmDefinition.key(), nicDefinition);
vmDefinitions.put(vmDefinition.key(), vmDefinition);
}
// Start the parallel creation of everything
computeManager.virtualMachines().createAsync(new ArrayList<>(vmDefinitions.values()))
.map(new Func1<Indexable, Indexable>() {
@Override
public Indexable call(Indexable createdResource) {
if (createdResource instanceof Resource) {
Resource resource = (Resource) createdResource;
System.out.println("Created: " + resource.id());
if (resource instanceof VirtualMachine) {
VirtualMachine virtualMachine = (VirtualMachine) resource;
// Record that this VM was created successfully
vmDefinitions.remove(virtualMachine.key());
// Remove the associated resources from cleanup list
vmNonNicResourceDefinitions.remove(virtualMachine.key());
// Remove the associated NIC from cleanup list
nicDefinitions.remove(virtualMachine.key());
} else {
// Add this related resource to potential cleanup list
createdResourceIds.put(resource.key(), resource.id());
}
}
return createdResource;
}
})
.onErrorReturn(new Func1<Throwable, Indexable>() {
@Override
public Indexable call(Throwable throwable) {
throwable.printStackTrace();
return null;
}
}).toBlocking().last();
// Delete remaining successfully created NICs of failed VM creations
Collection<String> nicIdsToDelete = new ArrayList<>();
for (Creatable<NetworkInterface> nicDefinition : nicDefinitions.values()) {
String nicId = createdResourceIds.get(nicDefinition.key());
if (nicId != null) {
nicIdsToDelete.add(nicId);
}
}
networkManager.networkInterfaces().deleteByIds(nicIdsToDelete);
// Delete remaining successfully created resources of failed VM creations
Collection<Completable> deleteObservables = new ArrayList<>();
for (Collection<Creatable<? extends Resource>> relatedResources : vmNonNicResourceDefinitions.values()) {
for (Creatable<? extends Resource> resource : relatedResources) {
String createdResourceId = createdResourceIds.get(resource.key());
if (createdResourceId != null) {
deleteObservables.add(resourceManager.genericResources().deleteByIdAsync(createdResourceId));
}
}
}
// Delete as much as possible, postponing the errors till the end
Completable.mergeDelayError(deleteObservables).await();
System.out.println("Number of failed/cleaned up VM creations: " + vmNonNicResourceDefinitions.size());
// Verifications
final int successfulVMCount = desiredVMCount - vmNonNicResourceDefinitions.size();
final int actualVMCount = computeManager.virtualMachines().listByResourceGroup(resourceGroupName).size();
Assert.assertEquals(successfulVMCount, actualVMCount);
final int actualNicCount = networkManager.networkInterfaces().listByResourceGroup(resourceGroupName).size();
Assert.assertEquals(successfulVMCount, actualNicCount);
final int actualNetworkCount = networkManager.networks().listByResourceGroup(resourceGroupName).size();
Assert.assertEquals(successfulVMCount, actualNetworkCount);
final int actualPipCount = networkManager.publicIPAddresses().listByResourceGroup(resourceGroupName).size();
Assert.assertEquals(successfulVMCount, actualPipCount);
final int actualAvailabilitySetCount = computeManager.availabilitySets().listByResourceGroup(resourceGroupName).size();
Assert.assertEquals(successfulVMCount, actualAvailabilitySetCount);
final int actualStorageAccountCount = storageManager.storageAccounts().listByResourceGroup(resourceGroupName).size();
Assert.assertEquals(successfulVMCount, actualStorageAccountCount);
// Verify that at least one VM failed.
// TODO: Ideally only one, but today the internal RX logic terminates eagerly -- need to change that for parallel creation to terminate more "lazily" in the future
Assert.assertTrue(successfulVMCount < desiredVMCount);
}
@Test
public void canCreateVirtualMachine() throws Exception {
// Create
computeManager.virtualMachines()
.define(VMNAME)
.withRegion(REGION)
.withNewResourceGroup(RG_NAME)
.withNewPrimaryNetwork("10.0.0.0/28")
.withPrimaryPrivateIPAddressDynamic()
.withoutPrimaryPublicIPAddress()
.withPopularWindowsImage(KnownWindowsVirtualMachineImage.WINDOWS_SERVER_2012_DATACENTER)
.withAdminUsername("Foo12")
.withAdminPassword("abc!@#F0orL")
.withUnmanagedDisks()
.withSize(VirtualMachineSizeTypes.STANDARD_D3)
.withOSDiskCaching(CachingTypes.READ_WRITE)
.withOSDiskName("javatest")
.create();
VirtualMachine foundVM = null;
List<VirtualMachine> vms = computeManager.virtualMachines().listByResourceGroup(RG_NAME);
for (VirtualMachine vm1 : vms) {
if (vm1.name().equals(VMNAME)) {
foundVM = vm1;
break;
}
}
Assert.assertNotNull(foundVM);
Assert.assertEquals(REGION, foundVM.region());
// Get
foundVM = computeManager.virtualMachines().getByResourceGroup(RG_NAME, VMNAME);
Assert.assertNotNull(foundVM);
Assert.assertEquals(REGION, foundVM.region());
// Fetch instance view
PowerState powerState = foundVM.powerState();
Assert.assertEquals(powerState, PowerState.RUNNING);
VirtualMachineInstanceView instanceView = foundVM.instanceView();
Assert.assertNotNull(instanceView);
Assert.assertNotNull(instanceView.statuses().size() > 0);
// Delete VM
computeManager.virtualMachines().deleteById(foundVM.id());
}
@Test
public void canCreateVirtualMachinesAndRelatedResourcesInParallel() throws Exception {
String vmNamePrefix = "vmz";
String publicIpNamePrefix = generateRandomResourceName("pip-", 15);
String networkNamePrefix = generateRandomResourceName("vnet-", 15);
int count = 5;
CreatablesInfo creatablesInfo = prepareCreatableVirtualMachines(REGION,
vmNamePrefix,
networkNamePrefix,
publicIpNamePrefix,
count);
List<Creatable<VirtualMachine>> virtualMachineCreatables = creatablesInfo.virtualMachineCreatables;
List<String> networkCreatableKeys = creatablesInfo.networkCreatableKeys;
List<String> publicIpCreatableKeys = creatablesInfo.publicIpCreatableKeys;
CreatedResources<VirtualMachine> createdVirtualMachines = computeManager.virtualMachines().create(virtualMachineCreatables);
Assert.assertTrue(createdVirtualMachines.size() == count);
Set<String> virtualMachineNames = new HashSet<>();
for (int i = 0; i < count; i++) {
virtualMachineNames.add(String.format("%s-%d", vmNamePrefix, i));
}
for (VirtualMachine virtualMachine : createdVirtualMachines.values()) {
Assert.assertTrue(virtualMachineNames.contains(virtualMachine.name()));
Assert.assertNotNull(virtualMachine.id());
}
Set<String> networkNames = new HashSet<>();
for (int i = 0; i < count; i++) {
networkNames.add(String.format("%s-%d", networkNamePrefix, i));
}
for (String networkCreatableKey : networkCreatableKeys) {
Network createdNetwork = (Network) createdVirtualMachines.createdRelatedResource(networkCreatableKey);
Assert.assertNotNull(createdNetwork);
Assert.assertTrue(networkNames.contains(createdNetwork.name()));
}
Set<String> publicIPAddressNames = new HashSet<>();
for (int i = 0; i < count; i++) {
publicIPAddressNames.add(String.format("%s-%d", publicIpNamePrefix, i));
}
for (String publicIpCreatableKey : publicIpCreatableKeys) {
PublicIPAddress createdPublicIPAddress = (PublicIPAddress) createdVirtualMachines.createdRelatedResource(publicIpCreatableKey);
Assert.assertNotNull(createdPublicIPAddress);
Assert.assertTrue(publicIPAddressNames.contains(createdPublicIPAddress.name()));
}
}
@Test
public void canStreamParallelCreatedVirtualMachinesAndRelatedResources() throws Exception {
String vmNamePrefix = "vmz";
String publicIpNamePrefix = generateRandomResourceName("pip-", 15);
String networkNamePrefix = generateRandomResourceName("vnet-", 15);
int count = 5;
final Set<String> virtualMachineNames = new HashSet<>();
for (int i = 0; i < count; i++) {
virtualMachineNames.add(String.format("%s-%d", vmNamePrefix, i));
}
final Set<String> networkNames = new HashSet<>();
for (int i = 0; i < count; i++) {
networkNames.add(String.format("%s-%d", networkNamePrefix, i));
}
final Set<String> publicIPAddressNames = new HashSet<>();
for (int i = 0; i < count; i++) {
publicIPAddressNames.add(String.format("%s-%d", publicIpNamePrefix, i));
}
final CreatablesInfo creatablesInfo = prepareCreatableVirtualMachines(REGION,
vmNamePrefix,
networkNamePrefix,
publicIpNamePrefix,
count);
final AtomicInteger resourceCount = new AtomicInteger(0);
List<Creatable<VirtualMachine>> virtualMachineCreatables = creatablesInfo.virtualMachineCreatables;
computeManager.virtualMachines().createAsync(virtualMachineCreatables)
.map(new Func1<Indexable, Indexable>() {
@Override
public Indexable call(Indexable createdResource) {
if (createdResource instanceof Resource) {
Resource resource = (Resource) createdResource;
System.out.println("Created: " + resource.id());
if (resource instanceof VirtualMachine) {
VirtualMachine virtualMachine = (VirtualMachine) resource;
Assert.assertTrue(virtualMachineNames.contains(virtualMachine.name()));
Assert.assertNotNull(virtualMachine.id());
} else if (resource instanceof Network) {
Network network = (Network) resource;
Assert.assertTrue(networkNames.contains(network.name()));
Assert.assertNotNull(network.id());
} else if (resource instanceof PublicIPAddress) {
PublicIPAddress publicIPAddress = (PublicIPAddress) resource;
Assert.assertTrue(publicIPAddressNames.contains(publicIPAddress.name()));
Assert.assertNotNull(publicIPAddress.id());
}
}
resourceCount.incrementAndGet();
return createdResource;
}
}).toBlocking().last();
// 1 resource group, 1 storage, 5 network, 5 publicIp, 5 nic, 5 virtual machines
// Additional one for CreatableUpdatableResourceRoot.
// TODO - ans - We should not emit CreatableUpdatableResourceRoot.
Assert.assertEquals(resourceCount.get(), 23);
}
private CreatablesInfo prepareCreatableVirtualMachines(Region region,
String vmNamePrefix,
String networkNamePrefix,
String publicIpNamePrefix,
int vmCount) {
Creatable<ResourceGroup> resourceGroupCreatable = resourceManager.resourceGroups()
.define(RG_NAME)
.withRegion(region);
Creatable<StorageAccount> storageAccountCreatable = storageManager.storageAccounts()
.define(generateRandomResourceName("stg", 20))
.withRegion(region)
.withNewResourceGroup(resourceGroupCreatable);
List<String> networkCreatableKeys = new ArrayList<>();
List<String> publicIpCreatableKeys = new ArrayList<>();
List<Creatable<VirtualMachine>> virtualMachineCreatables = new ArrayList<>();
for (int i = 0; i < vmCount; i++) {
Creatable<Network> networkCreatable = networkManager.networks()
.define(String.format("%s-%d", networkNamePrefix, i))
.withRegion(region)
.withNewResourceGroup(resourceGroupCreatable)
.withAddressSpace("10.0.0.0/28");
networkCreatableKeys.add(networkCreatable.key());
Creatable<PublicIPAddress> publicIPAddressCreatable = networkManager.publicIPAddresses()
.define(String.format("%s-%d", publicIpNamePrefix, i))
.withRegion(region)
.withNewResourceGroup(resourceGroupCreatable);
publicIpCreatableKeys.add(publicIPAddressCreatable.key());
Creatable<VirtualMachine> virtualMachineCreatable = computeManager.virtualMachines()
.define(String.format("%s-%d", vmNamePrefix, i))
.withRegion(region)
.withNewResourceGroup(resourceGroupCreatable)
.withNewPrimaryNetwork(networkCreatable)
.withPrimaryPrivateIPAddressDynamic()
.withNewPrimaryPublicIPAddress(publicIPAddressCreatable)
.withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_16_04_LTS)
.withRootUsername("tirekicker")
.withRootPassword("BaR@12!#")
.withUnmanagedDisks()
.withNewStorageAccount(storageAccountCreatable);
virtualMachineCreatables.add(virtualMachineCreatable);
}
CreatablesInfo creatablesInfo = new CreatablesInfo();
creatablesInfo.virtualMachineCreatables = virtualMachineCreatables;
creatablesInfo.networkCreatableKeys = networkCreatableKeys;
creatablesInfo.publicIpCreatableKeys = publicIpCreatableKeys;
return creatablesInfo;
}
class CreatablesInfo {
public List<Creatable<VirtualMachine>> virtualMachineCreatables;
List<String> networkCreatableKeys;
List<String> publicIpCreatableKeys;
}
}