Skip to content

Commit e84d68d

Browse files
authored
mgmt, support creating virtual machine with scale set (Azure#27613)
* mgmt, support creating virtual machine with scale set * nit, javadoc fix * checkstyle * nit, changelog
1 parent a6e372c commit e84d68d

File tree

5 files changed

+2359
-1
lines changed

5 files changed

+2359
-1
lines changed

sdk/resourcemanager/azure-resourcemanager-compute/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- Supported disk encryption set in `Disk` and `VirtualMachine`.
1818
- Changed to use PATCH for `GalleryImage` update.
1919
- Supported ephemeral OS disk in `VirtualMachine`.
20+
- Supported creating `VirtualMachine` with existing `VirtualMachineScaleSet`.
2021

2122
### Other Changes
2223

sdk/resourcemanager/azure-resourcemanager-compute/src/main/java/com/azure/resourcemanager/compute/implementation/VirtualMachineImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import com.azure.resourcemanager.compute.models.VirtualMachineIdentity;
7373
import com.azure.resourcemanager.compute.models.VirtualMachineInstanceView;
7474
import com.azure.resourcemanager.compute.models.VirtualMachinePriorityTypes;
75+
import com.azure.resourcemanager.compute.models.VirtualMachineScaleSet;
7576
import com.azure.resourcemanager.compute.models.VirtualMachineSize;
7677
import com.azure.resourcemanager.compute.models.VirtualMachineSizeTypes;
7778
import com.azure.resourcemanager.compute.models.VirtualMachineUnmanagedDataDisk;
@@ -1809,6 +1810,14 @@ public String availabilitySetId() {
18091810
return null;
18101811
}
18111812

1813+
@Override
1814+
public String virtualMachineScaleSetId() {
1815+
if (innerModel().virtualMachineScaleSet() != null) {
1816+
return innerModel().virtualMachineScaleSet().id();
1817+
}
1818+
return null;
1819+
}
1820+
18121821
@Override
18131822
public String provisioningState() {
18141823
return innerModel().provisioningState();
@@ -2681,6 +2690,14 @@ public VirtualMachineImpl withPlacement(DiffDiskPlacement placement) {
26812690
return this;
26822691
}
26832692

2693+
@Override
2694+
public VirtualMachineImpl withExistingVirtualMachineScaleSet(VirtualMachineScaleSet scaleSet) {
2695+
if (scaleSet != null) {
2696+
this.innerModel().withVirtualMachineScaleSet(new SubResource().withId(scaleSet.id()));
2697+
}
2698+
return this;
2699+
}
2700+
26842701
/** Class to manage Data disk collection. */
26852702
private class ManagedDataDiskCollection {
26862703
private final Map<String, DataDisk> newDisksToAttach = new HashMap<>();

sdk/resourcemanager/azure-resourcemanager-compute/src/main/java/com/azure/resourcemanager/compute/models/VirtualMachine.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,9 @@ Mono<RunCommandResult> runShellScriptAsync(
311311
/** @return the resource ID of the availability set associated with this virtual machine */
312312
String availabilitySetId();
313313

314+
/** @return the resource ID of the virtual machine scale set associated with this virtual machine */
315+
String virtualMachineScaleSetId();
316+
314317
/** @return the provisioningState value */
315318
String provisioningState();
316319

@@ -1765,6 +1768,16 @@ interface WithAvailabilityZone {
17651768
WithManagedCreate withAvailabilityZone(AvailabilityZoneId zoneId);
17661769
}
17671770

1771+
/** The stage of the VM definition allowing to specify virtual machine scale set */
1772+
interface WithScaleSet {
1773+
/**
1774+
* Specifies an existing virtual machine scale set for the virtual machine.
1775+
* @param scaleSet the virtual machine scale set with flexible orchestration mode
1776+
* @return the next stage of the definition
1777+
*/
1778+
WithManagedCreate withExistingVirtualMachineScaleSet(VirtualMachineScaleSet scaleSet);
1779+
}
1780+
17681781
/**
17691782
* The stage of the definition which contains all the minimum required inputs for the VM using managed OS disk
17701783
* to be created and optionally allow managed data disks specific settings to be specified.
@@ -1874,7 +1887,8 @@ interface WithCreate
18741887
DefinitionStages.WithLicenseType,
18751888
DefinitionStages.WithAdditionalCapacities,
18761889
DefinitionStages.WithNetworkInterfaceDeleteOptions,
1877-
DefinitionStages.WithEphemeralOSDisk {
1890+
DefinitionStages.WithEphemeralOSDisk,
1891+
DefinitionStages.WithScaleSet {
18781892

18791893
/**
18801894
* Begins creating the virtual machine resource.

sdk/resourcemanager/azure-resourcemanager-compute/src/test/java/com/azure/resourcemanager/compute/VirtualMachineOperationsTests.java

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.azure.core.util.CoreUtils;
1313
import com.azure.core.util.polling.LongRunningOperationStatus;
1414
import com.azure.core.util.polling.PollResponse;
15+
import com.azure.resourcemanager.compute.models.ApiErrorException;
1516
import com.azure.resourcemanager.compute.models.AvailabilitySet;
1617
import com.azure.resourcemanager.compute.models.CachingTypes;
1718
import com.azure.resourcemanager.compute.models.DeleteOptions;
@@ -25,16 +26,22 @@
2526
import com.azure.resourcemanager.compute.models.ProximityPlacementGroupType;
2627
import com.azure.resourcemanager.compute.models.RunCommandInputParameter;
2728
import com.azure.resourcemanager.compute.models.RunCommandResult;
29+
import com.azure.resourcemanager.compute.models.UpgradeMode;
2830
import com.azure.resourcemanager.compute.models.VirtualMachine;
2931
import com.azure.resourcemanager.compute.models.VirtualMachineEvictionPolicyTypes;
3032
import com.azure.resourcemanager.compute.models.VirtualMachineInstanceView;
3133
import com.azure.resourcemanager.compute.models.VirtualMachinePriorityTypes;
34+
import com.azure.resourcemanager.compute.models.VirtualMachineScaleSet;
35+
import com.azure.resourcemanager.compute.models.VirtualMachineScaleSetSkuTypes;
3236
import com.azure.resourcemanager.compute.models.VirtualMachineSizeTypes;
3337
import com.azure.resourcemanager.compute.models.VirtualMachineUnmanagedDataDisk;
38+
import com.azure.resourcemanager.network.models.LoadBalancer;
39+
import com.azure.resourcemanager.network.models.LoadBalancerSkuType;
3440
import com.azure.resourcemanager.network.models.Network;
3541
import com.azure.resourcemanager.network.models.NetworkInterface;
3642
import com.azure.resourcemanager.network.models.NetworkSecurityGroup;
3743
import com.azure.resourcemanager.network.models.NicIpConfiguration;
44+
import com.azure.resourcemanager.network.models.PublicIPSkuType;
3845
import com.azure.resourcemanager.network.models.PublicIpAddress;
3946
import com.azure.resourcemanager.network.models.SecurityRuleProtocol;
4047
import com.azure.resourcemanager.network.models.Subnet;
@@ -1305,6 +1312,139 @@ public void canCreateVirtualMachineWithEphemeralOSDisk() {
13051312
Assertions.assertThrows(Exception.class, vm::deallocate);
13061313
}
13071314

1315+
@Test
1316+
public void canCreateVirtualMachineWithExistingScaleSet() throws Exception {
1317+
// can add regular vm to vmss
1318+
final String vmssName = generateRandomResourceName("vmss", 10);
1319+
Network network =
1320+
this
1321+
.networkManager
1322+
.networks()
1323+
.define("vmssvnet")
1324+
.withRegion(region.name())
1325+
.withNewResourceGroup(rgName)
1326+
.withAddressSpace("10.0.0.0/28")
1327+
.withSubnet("subnet1", "10.0.0.0/28")
1328+
.create();
1329+
ResourceGroup resourceGroup = this.resourceManager.resourceGroups().getByName(rgName);
1330+
LoadBalancer publicLoadBalancer = createHttpLoadBalancers(region, resourceGroup, "1", LoadBalancerSkuType.STANDARD, PublicIPSkuType.STANDARD, true);
1331+
VirtualMachineScaleSet flexibleVMSS = this.computeManager
1332+
.virtualMachineScaleSets()
1333+
.define(vmssName)
1334+
.withRegion(region)
1335+
.withExistingResourceGroup(rgName)
1336+
.withFlexibleOrchestrationMode()
1337+
.withSku(VirtualMachineScaleSetSkuTypes.STANDARD_DS1_V2)
1338+
.withExistingPrimaryNetworkSubnet(network, "subnet1")
1339+
.withExistingPrimaryInternetFacingLoadBalancer(publicLoadBalancer)
1340+
.withoutPrimaryInternalLoadBalancer()
1341+
.withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_16_04_LTS)
1342+
.withRootUsername("jvuser")
1343+
.withSsh(sshPublicKey())
1344+
.withCapacity(1)
1345+
.withUpgradeMode(UpgradeMode.AUTOMATIC)
1346+
.create();
1347+
1348+
String regularVMName = generateRandomResourceName("vm", 10);
1349+
final String pipDnsLabel = generateRandomResourceName("pip", 10);
1350+
VirtualMachine regularVM = this.computeManager
1351+
.virtualMachines()
1352+
.define(regularVMName)
1353+
.withRegion(region)
1354+
.withNewResourceGroup(rgName)
1355+
.withNewPrimaryNetwork("10.0.1.0/28")
1356+
.withPrimaryPrivateIPAddressDynamic()
1357+
.withNewPrimaryPublicIPAddress(pipDnsLabel)
1358+
.withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_18_04_LTS)
1359+
.withRootUsername("jvuser2")
1360+
.withSsh(sshPublicKey())
1361+
.withExistingVirtualMachineScaleSet(flexibleVMSS)
1362+
.create();
1363+
flexibleVMSS.refresh();
1364+
Assertions.assertEquals(flexibleVMSS.id(), regularVM.virtualMachineScaleSetId());
1365+
Assertions.assertEquals(2, flexibleVMSS.capacity());
1366+
// Flexible vmss vm instance ids are all null, which means VMs in flexible vmss can only be operated by individual `VirtualMachine` APIs.
1367+
Assertions.assertTrue(flexibleVMSS.virtualMachines().list().stream().allMatch(vm -> vm.instanceId() == null));
1368+
1369+
regularVM.deallocate();
1370+
Assertions.assertEquals(regularVM.powerState(), PowerState.DEALLOCATED);
1371+
1372+
this.computeManager
1373+
.virtualMachines().deleteById(regularVM.id());
1374+
flexibleVMSS.refresh();
1375+
Assertions.assertEquals(flexibleVMSS.capacity(), 1);
1376+
1377+
// can't add vm with unmanaged disk to vmss
1378+
final String storageAccountName = generateRandomResourceName("stg", 17);
1379+
Assertions.assertThrows(
1380+
ApiErrorException.class,
1381+
() -> computeManager
1382+
.virtualMachines()
1383+
.define(vmName)
1384+
.withRegion(region)
1385+
.withNewResourceGroup(rgName)
1386+
.withNewPrimaryNetwork("10.0.1.0/28")
1387+
.withPrimaryPrivateIPAddressDynamic()
1388+
.withoutPrimaryPublicIPAddress()
1389+
.withLatestLinuxImage("Canonical", "UbuntuServer", "14.04.2-LTS")
1390+
.withRootUsername("jvuser3")
1391+
.withSsh(sshPublicKey())
1392+
.withUnmanagedDisks() /* UN-MANAGED OS and DATA DISKS */
1393+
.withSize(VirtualMachineSizeTypes.fromString("Standard_D2a_v4"))
1394+
.withNewStorageAccount(storageAccountName)
1395+
.withOSDiskCaching(CachingTypes.READ_WRITE)
1396+
.withExistingVirtualMachineScaleSet(flexibleVMSS)
1397+
.create()
1398+
);
1399+
1400+
// can't add vm to `UNIFORM` vmss
1401+
final String vmssName2 = generateRandomResourceName("vmss", 10);
1402+
Network network2 =
1403+
this
1404+
.networkManager
1405+
.networks()
1406+
.define("vmssvnet2")
1407+
.withRegion(region.name())
1408+
.withExistingResourceGroup(rgName)
1409+
.withAddressSpace("192.168.0.0/28")
1410+
.withSubnet("subnet2", "192.168.0.0/28")
1411+
.create();
1412+
LoadBalancer publicLoadBalancer2 = createHttpLoadBalancers(region, resourceGroup, "2", LoadBalancerSkuType.STANDARD, PublicIPSkuType.STANDARD, true);
1413+
VirtualMachineScaleSet uniformVMSS = this.computeManager
1414+
.virtualMachineScaleSets()
1415+
.define(vmssName2)
1416+
.withRegion(region)
1417+
.withNewResourceGroup(rgName)
1418+
.withSku(VirtualMachineScaleSetSkuTypes.STANDARD_A0)
1419+
.withExistingPrimaryNetworkSubnet(network2, "subnet2")
1420+
.withExistingPrimaryInternetFacingLoadBalancer(publicLoadBalancer2)
1421+
.withoutPrimaryInternalLoadBalancer()
1422+
.withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_16_04_LTS)
1423+
.withRootUsername("jvuser4")
1424+
.withSsh(sshPublicKey())
1425+
.withCapacity(1)
1426+
.create();
1427+
Assertions.assertTrue(uniformVMSS.virtualMachines().list().stream().allMatch(v -> v.instanceId() != null));
1428+
1429+
String regularVMName2 = generateRandomResourceName("vm", 10);
1430+
Assertions.assertThrows(
1431+
ApiErrorException.class,
1432+
() -> this.computeManager
1433+
.virtualMachines()
1434+
.define(regularVMName2)
1435+
.withRegion(region)
1436+
.withNewResourceGroup(rgName)
1437+
.withNewPrimaryNetwork("10.0.1.0/28")
1438+
.withPrimaryPrivateIPAddressDynamic()
1439+
.withoutPrimaryPublicIPAddress()
1440+
.withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_18_04_LTS)
1441+
.withRootUsername("jvuser5")
1442+
.withSsh(sshPublicKey())
1443+
.withExistingVirtualMachineScaleSet(uniformVMSS)
1444+
.create()
1445+
);
1446+
}
1447+
13081448
private CreatablesInfo prepareCreatableVirtualMachines(
13091449
Region region, String vmNamePrefix, String networkNamePrefix, String publicIpNamePrefix, int vmCount) {
13101450

0 commit comments

Comments
 (0)