First, the Synology DSM version being used during both tests was 7.2-64570 Update 1. I mention this as performance improvements for iSCSI or NFS could occur between DSM releases which could greatly impact the results.
Second, the previous tests focused only on VAAI offload performance. With the exception of the first test, which copied a file from local disk to the Synology DS723+, I didn’t get a feel for if read/write operations within the guests would have any performance differences.
I’m not a huge fan of IO benchmark tools, as they can be tweaked and biased towards a specific configuration. Instead, I came up with three quick tests to look at real world things that I could measure against a specific guest VM. Since some of these tests also require CPU/memory, I performed them one at a time, on a host running no other workloads, and did each test 3 times to find the average runtime. These tests were completed from clones of the same Windows 2022 virtual machine, running on the same ESXi 8.0u1 host.
There are likely more sophisticated tests that could be performed. As outlined in the previous post, this NAS is currently connected with a single 1Gbps NIC and has no SSD. Those are knobs that could be adjusted to impact performance, but for comparisons sake the only variable intentionally adjusted for the tests below is whether the VM resides on NFS or iSCSI backed storage.
Test1: Power on a Windows 2022 virtual machine, which should be primarily a read IO operation. I measured this with Measure-Command { Get-VM TestVM | Start-VM | Wait-Tools} | Select-Object TotalSeconds.
Test2: Update VMware Tools, which should be primarily a write operation (to update/replace existing files). I measured this with Measure-Command { Get-VM TestVM | Update-Tools } | Select-Object TotalSeconds.
Test3: File copy within the guest OS, which should be equal parts read & write. For this I picked an existing folder on the system with lots of small files and copied it to another disk in the system, both backed by the same Synology storage. The source folder was 6.4GB with 66,544 files in 25,572 folders. I measured this with Measure-Command { robocopy C:\Windows\winsxs D:\winsxs /MT:4 /S /DCOPY:DT } | Select-Object TotalSeconds
Test Type
Storage
Time1
Time2
Time3
Average
Power On
NFS
57.79
52.67
52.68
54.38
Power On
iSCSI
58.82
53.75
47.69
53.42
Tools Upgrade
NFS
74.97
97.9
94.42
89.10
Tools Upgrade
iSCSI
89.13
92.95
80.42
87.50
Robocopy
NFS
799
763
757
773
Robocopy
iSCSI
609
679
686
658
Guest Operation performance tests, all values in seconds
Summary: Based on the above table, we can see that the first two tests have very similar results with no noticeable difference between NFS and iSCSI performance. The average time for the two tasks (power on & tools upgrade) are within an acceptable margin of error. For the last test, using robocopy to duplicate a lot of small files, the VM on an iSCSI datastore completed the task about 2 minutes faster (on average) than the VM on an NFS datastore. As with the previous test, this is not what I had expected.
I recently got my hands on a new Synology DS723+ and a couple of 4TB HDD disks for my home lab. My plan is to use this device as a backup target, either as a storage destination for my existing backup product, or perhaps switching to the Synology Active Backup application that can be enabled on the Synology device. I’ll be doing a bit more testing with this in the future, and do plan to write up those results in a future post.
However, since I had this new device, I wanted to revisit a decision I made several years ago — whether I should use iSCSI or NFS for my ESXi Datastores. I wanted to do a side-by-side comparison of the protocols to make sure I was using the optimial one. I also wanted to test NFS with & without the NFS VAAI plugin, to make sure it was worth installing as part of host builds. Several years ago, I purchased a Synology DS1817+ to use as the primary storage in my lab and decided to use NFS with the VAAI plugin. This has worked very well for me, but it never hurts to re-think a decision.
For this test I decided to perform a couple of virtual machine copy and move operations. I wanted to use the same virtual machine, but decided to use one smaller template and one that was a bit larger. Here are some storage details on the two VMs that I used for comparisons:
photon5 – 16GB VMDK, thin provisioned & using about 860MB on disk
win22 – 50GB VMDK, thin provisioned & using about 16GB on disk
Both test VMs were placed on a local NVME disk on a test host which also had a pair of NFS datastores and a pair of iSCSI datastores backed by the Synology DS723+, connected to the network with a single 1GbE port. The DS723+ has two gigabit connections available and can be upgraded to include a 10GbE connection. However, for this test I wanted to focus specifically on storage protocols and having this bottleneck in the network and using HDD instead of SSD seemed acceptable. Changing these factors would improve performance, but my focus of this test was the protocol & VAAI impact instead of pure performance.
To execute the tests & capture results, I decided to use PowerCLI commands inside of a Measure-Command code block and focus on total seconds. I had originally started this test using ESXi 7.0 only, but after seeing another thread mention that the Synology NFS VAAI VIB would work with vSphere 8.0 (although it is not officially supported), I decided to double up all the tests to see if drastic differences existed between the two versions. From the testing below, we can see that the ESXi version had no noticable impact on the testing. I listed the total seconds for the first test only, but did see some variance in multiple runs. I suspect if we averaged the results across 10 runs they would be even closer to the same value.
Test1: Full VM Copy across the network – completed & measured using syntax similar to: Measure-Command {New-VM -VM 'localdisk_photon5' -Name 'nfs_photon5' -VMHost 'testhost' -Datastore 'nfs1'} | Select-Object TotalSeconds
VM
ESXi
Source DS
Dest DS
Time (seconds)
photon5
7.0u3
Local
NFS
12.25
photon5
8.0u1
Local
NFS
13.80
photon5
7.0u3
Local
iSCSI
10.98
photon5
8.0u1
Local
iSCSI
11.96
win22
7.0u3
Local
NFS
169.53
win22
8.0u1
Local
NFS
164.36
win22
7.0u3
Local
iSCSI
178.77
win22
8.0u1
Local
iSCSI
168.55
Results of Test Case 1
Based on the table above, we can see that the performance of all the New-VM operations are roughly the same for the same per source VM size.
Test2: Full VM Copy between datastores of the same type – completed & measured using syntax similar to: Measure-Command { Move-VM -VM 'nfs_photon5' -Datastore 'nfs2' } | Select-Object TotalSeconds
VM
ESXi
Source DS
Dest DS
Time (seconds)
photon5
7.0u3
NFS
NFS
428.32
photon5
8.0u1
NFS
NFS
430.61
photon5
7.0u3
NFS (VAAI)
NFS (VAAI)
22.31
photon5
8.0u1
NFS (VAAI)
NFS (VAAI)
21.97
photon5
7.0u3
iSCSI
iSCSI
2.45
photon5
8.0u1
iSCSI
iSCSI
3.56
win22
7.0u3
NFS
NFS
1384.99
win22
8.0u1
NFS
NFS
1387.60
win22
7.0u3
NFS (VAAI)
NFS (VAAI)
231.77
win22
8.0u1
NFS (VAAI)
NFS (VAAI)
225.20
win22
7.0u3
iSCSI
iSCSI
4.54
win22
8.0u1
iSCSI
iSCSI
4.56
Results of Test Case 2
I did not include specific results, but the tests observed above for Move-VM had similar results to New-VM copies that used the same source/destination datastores. For example, a Windows 2022 New-VM from iSCSI to iSCSI (similar to the last 2 rows of the above table) only took 3.83 seconds. This is most likely due to the fact that both clone and migrate requests are using the XCOPY VAAI primitive, described here: https://core.vmware.com/resource/vmware-vsphere-apis-array-integration-vaai#sec9410-sub4.
Based on the table above, we observe some differences for NFS vs. iSCSI. Installing the NFS VAAI plugin offered a significant improvement vs. the default NFS configuration. However, the VAAI iSCSI performance for Move-VM operations was noticeably better than the best case NFS configuartion.
Summary: These are not the results I had initially expected. I had assumed NFS performance with VAAI enabled would have been much closer to the iSCSI performance numbers. I was very impressed by the iSCSI performance for clones/moves when using VAAI. Based on Test 1, I’m likely to leave the majority of VMs already deployed in their current NFS configuration. I don’t move the VMs around and the VMs aren’t observing any latency, so I can’t think of a compelling reason to go back & move them. However, based on Test 2, I do plan to make some changes. I currently have a couple Aria Automation deployments that use linked clones to improve provisioning times. I’m likely to revisit that configuration, moving the template VMs & target deployments to iSCSI datastores backed by the same Synology array to unlock the super-fast cloning.
Have you ever had a new site come online and want to validate that the items in a subscriber content library are in sync? Or experience some storage issues/corruption and want to make sure your sync’d library is still intact? This post will use a PowerCLI script to quickly validate items in a content library.
$contentLibraryName = 'h114-sub'
$testHost = 'lab-esxi-02.example.org'
$testDatastore = 'local-hdd'
foreach ($clitem in ( Get-ContentLibraryItem -ContentLibrary $contentLibraryName -ItemType ovf | Sort-Object Name ) ) {
"Starting deploy of item $($clitem.name)..."
$thisNewVM = $clitem | New-VM -Name "Test_$($clitem.Name)" -VMHost $testHost -Datastore $testDatastore
if ($thisNewVM) {
" New VM created. Will attempt power on."
[void]($thisNewVM | Get-NetworkAdapter | Set-NetworkAdapter -StartConnected:$false -confirm:$false)
$thisVMstarted = $thisNewVM | Start-VM | Wait-Tools
if ($thisVMstarted) {
" Startup success, doing cleanup."
[void]($thisVMstarted | Stop-VM -Confirm:$false)
[void]($thisVMstarted | Remove-VM -DeletePermanently -Confirm:$false)
} else {
" There was an issue with a test VM for content library item $($clitem.Name). Cleanup not completed."
}
} else {
write-warning " Failure occured creating new VM based on $($clitem.Name)!"
}
}
This script has three variables that must be defined — the name of the subscriber content library, a test host in the site/environment you wish to test, and a datastore from that test host to use for provisioning.
The script will retrieve a list of OVF templates, deploy a new VM from each using the creative name Test + the name of the content library item. Once the item is deployed, the VM will be powered on and wait for tools to start. If Tools start successfully, we can likely assume that the VM is in working order, so the script proceeds to power off/delete the test VM, otherwise it will throw a warning to the screen and continue.
During testing I encountered/created a few issues which I’ll describe below.
The first was caused by an ’empty’ VM which didn’t have an OS installed. It failed with an error that VMware Tools was not installed. The script didn’t delete this VM, but after reviewing an confirming this was expected behavior for the VM, I deleted it anyway.
Wait-Tools : 8/21/2023 6:53:45 AM Wait-Tools The specified VM 'Test_emptyVM-notools' does not have VMware Tools
installed.
At line:7 char:46
+ $thisVMstarted = $thisNewVM | Start-VM | Wait-Tools
+ ~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Test_emptyVM-notools:VirtualMachineImpl) [Wait-Tools], ViError
+ FullyQualifiedErrorId : Client20_VmGuestServiceImpl_WaitTools_ToolsNotInstalledOrVersionIncorrect,VMware.VimAuto
mation.ViCore.Cmdlets.Commands.WaitTools
To contrive the next error, I ssh’d to the test host, found the content library item on the filesystem, and intentionally corrupted the VMDK by running echo blah > emptyVM-notools-1_248c256e-cab1-4512-b7a0-48322ad4a7bf-flat.vmdk. This resulted in the following error (read below the next error for the resolution to this):
New-VM : 8/21/2023 6:15:29 AM New-VM Transfer failed: Error during transfer of
ds:///vmfs/volumes/639245e5-399bba0d-e1e8-000c2927a169//Test_emptyVM-notools/Test_emptyVM-notools.vmdk: The virtual
disk is either corrupted or not a supported format..
At line:3 char:26
+ ... = $clitem | New-VM -Name "Test_$($clitem.Name)" -VMHost $testHost -Da ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [New-VM], ViError
+ FullyQualifiedErrorId : ViCore_VMServiceImpl_DeployFromLibraryItem_ViNetException,VMware.VimAutomation.ViCore.Cm
dlets.Commands.NewVM
After capturing this error, I deleted the files associated with the content library item. I had assumed that these missing files would trigger the content library to re-sync the content. Instead, I received the following error:
New-VM : 8/21/2023 6:22:32 AM New-VM Transfer failed: Error during transfer of ds:///vmfs/volumes/639245e5-399bba0d-e1e8-000c2927a169//contentlib-21c270a1-11
7e-4982-a014-ac122fdb74eb/088a872b-1098-441b-807a-251f76402991/emptyVM-notools-1_248c256e-cab1-4512-b7a0-48322ad4a7bf.vmdk?serverId=bad2b868-9a1e-4d68-ba7e-26
0f397cc0ca: Failed to resolve source datastore file URI(ds:///vmfs/volumes/639245e5-399bba0d-e1e8-000c2927a169//contentlib-21c270a1-117e-4982-a014-ac122fdb74e
b/088a872b-1098-441b-807a-251f76402991/emptyVM-notools-1_248c256e-cab1-4512-b7a0-48322ad4a7bf.vmdk?serverId=bad2b868-9a1e-4d68-ba7e-260f397cc0ca).
At line:3 char:26
+ ... = $clitem | New-VM -Name "Test_$($clitem.Name)" -VMHost $testHost -Da ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [New-VM], ViError
+ FullyQualifiedErrorId : ViCore_VMServiceImpl_DeployFromLibraryItem_ViNetException,VMware.VimAutomation.ViCore.Cmdlets.Commands.NewVM
To address the previous two items, I found the ‘bad’ content library item in the web interface, right clicked and selected delete item content. I had expected this operation to fail, as I assumed that content could only be deleted from the publisher repo and not the subscriber repo. However, this command was successful, removing the ‘size’ property of the item from the content library as if it had never sync’d. Rerunning the script to deploy this content library item caused the item to replicate and the deployment was successful.
Hopefully this script can help you validate content library items if needed. If you have any suggestions on how to improve this script, please leave a comment.
In the May 2023/8.12.1 release notes for Aria Automation, one of the what’s new items was the ability to reserve/unreserve an IP address to make it unavailable/available for deployment. In this release the functionality is supported via API only. This post will cover how to connect to and consume this API
The first thing we’ll look at is the network elements from the web interface to show which object we’ll be interacting with from the API. Under Assembler > Infrastructure > Network Profiles we see various networks. In this case we’ll use VLAN40, which in my lab contains physical network devices.
We will open this tile, then select the Networks tab. From here we can see the 192.168.40.0/24 network.
We will select the Network and then click Manage IP Ranges.
This ‘VLAN40 – Network Devices’ name (which I intentionally set different that the network profile name above) is the one we see when calling the API for a list of network-ranges. I defined the range as being 192.168.40.10 through 192.168.40.199, for a total of 190 usable addresses. Now that we’ve seen the elements in the web interface, we will interact with them using this new API.
Our first task will be logging into the API. We will provide a username/password to obtain a refresh token, then we’ll use that token to obtain a bearer token. This is based on the postman example from this KB article: https://kb.vmware.com/s/article/89129.
We know from the web interface that the name of our network range is ‘VLAN40 – Network Devices’. We will use a where-object (using the question mark alias) to find the ID of this network.
$thisNetId=($networkIpRanges.content | ?{$_.Name -match 'vlan40'}).id
$networkIpRequest = Invoke-RestMethod -Uri "$($vraHost)/iaas/api/network-ip-ranges/$thisNetId/ip-addresses/allocate" -Method 'POST' -body $(@{description='I need some IPs for the demo app'; numberOfIps=2}|ConvertTo-Json) -ContentType 'application/json' -Headers @{Authorization="Bearer $($bearerToken.token)"}
# wait a few seconds for the request to complete, then ask for results
$reqId = $networkIpRequest.id
foreach ($thisResource in (Invoke-RestMethod -Uri "$($vraHost)/iaas/api/request-tracker/$reqId" -Method 'GET' -ContentType 'application/json' -Headers @{Authorization="Bearer $($bearerToken.token)"}).Resources) {
Invoke-RestMethod -Uri "$($vraHost)$($thisResource)?apiVersion=2021-07-15" -Method 'GET' -ContentType 'application/json' -Headers @{Authorization="Bearer $($bearerToken.token)"}
}
The networkIpRequest object that results will have an ID property to identify that specific network. In the codeblock below, we will use that ID to get the status of that request. Since we specified that we needed 2 addresses, the Resources property will be a collection of resource IDs. We will loop through the addresses and get more details on each item
The above code will return the details of the two IP addresses that were allocated, results below.
ipAddress : 192.168.40.10
ipAddressDecimalValue : 3232245770
ipVersion : IPv4
ipAddressStatus : ALLOCATED
ipAllocationType : USER
description : I need some IPs for the demo app
id : 3f025212-e50a-430e-a8f2-baf0e53deb8f
createdAt : 2023-08-28
updatedAt : 2023-08-28
orgId : e142c6b9-379b-4754-9790-00b4f4373ca4
_links : @{network-ip-range=; self=}
ipAddress : 192.168.40.11
ipAddressDecimalValue : 3232245771
ipVersion : IPv4
ipAddressStatus : ALLOCATED
ipAllocationType : USER
description : I need some IPs for the demo app
id : 32416121-39b5-4462-8d51-2bf6743368fa
createdAt : 2023-08-28
updatedAt : 2023-08-28
orgId : e142c6b9-379b-4754-9790-00b4f4373ca4
_links : @{network-ip-range=; self=}
As you can see, 192.168.40.10 and 192.168.40.11 are the first two addresses in the pool and those are the ones given to us for our demo app.
Lets assume that we ended up only needing one of those addresses and now want to return / release one to the pool. We can do this with the API as well. The following code needs the Network ID from above, as well as the specific address we want to release. In this example we will return 192.168.40.11.
This will again return a task ID which looks like this:
progress : 1
status : INPROGRESS
name : IP Address Release Task
id : ad5dc112-3dfc-4f7c-836a-6808084bcd56
selfLink : /iaas/api/request-tracker/ad5dc112-3dfc-4f7c-836a-6808084bcd56
To confirm this completed, we could post to the 'selfLink' to get a status, but instead we will look at our network-ip-ranage and see which addresses are in use. In this case we would only expect 1 address, the 192.168.40.10 IP.
(Invoke-RestMethod -Uri "$($vraHost)/iaas/api/network-ip-ranges/$thisNetId/ip-addresses?apiVersion=2021-07-15" -Method 'GET' -ContentType 'application/json' -Headers @{Authorization="Bearer $($bearerToken.token)"}).content
ipAddress : 192.168.40.10
ipAddressDecimalValue : 3232245770
ipVersion : IPv4
ipAddressStatus : ALLOCATED
ipAllocationType : USER
description : I need some IPs for the demo app
id : 3f025212-e50a-430e-a8f2-baf0e53deb8f
createdAt : 2023-08-28
updatedAt : 2023-08-28
orgId : e142c6b9-379b-4754-9790-00b4f4373ca4
_links : @{network-ip-range=; self=}
ipAddress : 192.168.40.11
ipAddressDecimalValue : 3232245771
ipVersion : IPv4
ipAddressStatus : RELEASED
ipAllocationType : NONE
description :
id : 32416121-39b5-4462-8d51-2bf6743368fa
createdAt : 2023-08-28
updatedAt : 2023-08-28
orgId : e142c6b9-379b-4754-9790-00b4f4373ca4
_links : @{network-ip-range=; self=}
As we can see above, the 192.168.40.10 address is allocated, while the 192.168.40.11 address has been released. In this case we can see that only 1 address is in use. If we are in the web interface (Assembler > Infrastructure > Network Profiles > specific tile > Networks > Manage IP Ranges) we can see the same — only 1 address is currently used.
I hope this walkthrough shows how to consume this new API to manage Aria Automation (formerly known as vRealize Automation) internal IPAM.
I was recently working with someone who was using vRealize Suite Lifecycle Manager 8.10 (now known as Aria Suite Lifecycle) to update vRealize Network Insight from version 6.7 to 6.9 (now known as Aria Operations for Networks). The Product Support Pack was updated to the correct version, the binary mapping task had been completed, and the environment trigger inventory sync task completed without issue. However the very next screen where one would normally select the desired version repository had an error which stated “no repositories found for given version.” Even more interestingly, the choices for product version only provided 5.2 and 5.3 as choices. We had expected to see versions 6.8 and 6.9 instead.
After validating we had the correct PSPak and binary again we happened to look at the Lifecycle Operations > Requests tab and sorted by Request Status. We noticed a VRNI upgrade task from this environment in pending action state from early 2021. This task had more options, including one option to delete the request.
On a hunch, we decided to delete this old task. After doing so, the upgrade wizard continued without showing the “no repositories” error, and the drop down list showed the expected versions 6.8 and 6.9.
In the requests UI, there were similar pending tasks for a few other products, like vRealize Automation and vRealize Operations. We didn’t validate, but expect that this issue could be present for other products/environments being updated from within vRealize Lifecycle Manager. To prevent future issues, we decided to delete those pending requests as well. We didn’t see any communities articles or blog posts on this issue, but since it was a rather easy fix I wanted to document it here. I hope this saves you a few minutes of trubleshooting!