After all this work with WNV I wrapped everything up with a lunch time training session covering the entire topic.
Quite honestly, it is a big concept to get across in 1 hour, wrapping the brain around the management IP, the PA addresses, the CA addresses and getting that all straight is the most confusing part – the first time someone has this tossed at them.
Once folks get those three items all sorted out it all seems to fall into place naturally.
The other idea is that fact that you are doing demonstrations and presentations as if you were a management layer. Not using a management layer, but you ARE the management layer. You have to do everything that you would expect a SCVMM / CloudStack / vCenter to normally be doing on your behalf.
Here are the assumptions for my demo:
Two 2012 Hyper-V Servers, four VMs. The VMs are Red11, Red22, Blue11, and Blue22. Red and Blue represent different Customers / Tenants. They are bringing their systems to my infrastructure and I will be hosting them on my two Hyper-V Servers.
Setup:
The VMs should have their IPs manually set in the following way and have PING enabled in the firewall.
- Red11 = 192.168.1.11, mask 255.255.255.0, gateway 192.168.1.1
- Red22 = 192.168.1.22, mask 255.255.255.0, gateway 192.168.1.1
- Blue11 = 192.168.1.11, mask 255.255.255.0, gateway 192.168.1.1
- Blue22 = 192.168.1.22, mask 255.255.255.0, gateway 192.168.1.1
The Hyper-V Servers should have one External Virtual network, with the VMs attached. The Hyper-V Servers need to be joined to the same domain with Migration enabled (we will be moving one VM). All VMs should be created on one of the Hyper-V Servers, this is also the Hyper-V Server where you will run the script.
And, the last point. When you prepare the demo to run, be sure to power on all VMs first, but power on the Red VMs first, then the Blue VMs. Red was on-boarded into my infrastructure first, and Blue’s VMs will catch an IP conflict.
Start-VM Red* -AsJob
sleep 60
Start-VM Blue* –AsJob
Once they are all running and report an IP address to the host, we are ready to go.
$vmNics = Get-VMNetworkAdapter *
do {
$upCount = ($vmNics | where {$_.IPAddresses.count -ne 0})
$upCount.Count
sleep 30
}
until ($vmNics.Count -eq $upCount.Count)
At this time you can begin running through the following scenario, one command set at a time. I used the PowerShell ISE and selected a block and then ran that block (F8). If you want to make all of these commands into one-liners then you can use Start-Demo.ps1.
Here is the meat, with all my commentary in the comments. Taking all the previous 6 posts and rolling them into something to demonstrate being the management layer.
<#
.SYNOPSIS
This is the scripted demonstration. Of Windows Network Virtualization
.DESCRIPTION
This assumes that there are two Server 2012 Hyper-V machines, joined to the
same domain. Both hypervisors have one External Virtual Switch.
This script is executed on one hypervisor, and has portions against the
remote hypervisor.
That local hypervisor has 4 (Server 2012) VMs named Red11, Red22, Blue11, Blue22.
On the remote hypervisor I used a PowerShell dedicated endpoint created
to work around CredSSP issues - http://blogs.msdn.com/b/taylorb/archive/2012/03/26/remote-administration-with-powershell-3-0-sessions-part-1.aspx
.EXAMPLE
For the demonstration the PowerShell v3 ISE was used to execute individual blocks of
commands.
.AUTHOR
Brian Ehlert
Senior Software Test Engineer 2
Citrix Labs, Redmond
#>
# Open a session to my remote host that I will simply re-use
$waldorf = New-PSSession waldorf.brianeh.local -ConfigurationName HVRemoteAdmin
Clear-Host
# This is just a demo, no fancy stuff. Everything is hard coded.
# Fancy scripting is for other examples.
# Red and Blue are customers that want me to host their VMs.
# There are four VMs Red1 Red2 Blue1 Blue2. (they need to live on one host and get along)
# They happen to have overlapping IP address schemes of 192.168.1.x
# IP Conflict - second powered up / set will not work. The OS auto disables and APIPA
Get-VMNetworkAdapter * | Format-Table VMName, Name, MACAddress, VirtualSubnetID, IPAddresses -AutoSize
# I could open a ticket with the Network folks to build a VLAN. This will take too long.
# I could force one of the clients to reconfigure their IP scheme. This is too complicated.
# Instead, I have set unique VirtualSubnetIDs for each customer (isolation boundary)
Get-VMNetworkAdapter Red* | Set-VMNetworkAdapter -VirtualSubnetId 445566
Get-VMNetworkAdapter Blue* | Set-VMNetworkAdapter -VirtualSubnetId 7788990
Get-VMNetworkAdapter * | Format-Table VMName, Name, MACAddress, VirtualSubnetID, IPAddresses -AutoSize
# IP Conflict is resolved.
# To clear the APIPA status and the default behavior of taking the conflicting IP offline:
Get-VMNetworkAdapter -VMName Blue* | Disconnect-VMNetworkAdapter
Get-VMNetworkAdapter -VMName Blue* | Connect-VMNetworkAdapter -SwitchName VMs
Get-VMNetworkAdapter * | Format-Table VMName, Name, MACAddress, VirtualSubnetID, IPAddresses -AutoSize
# Start a ping from Red11 to Red22
vmconnect Statler.brianeh.local Red11
# This works until I have to distribute VMs because one host is not enough capacity to carry everything for both customers.
Move-VM -Name “Red22” -DestinationHost Waldorf.brianeh.local –IncludeStorage –DestinationStoragePath D:\Red22
# ping is now broken as there is no way to route this additional tag
# So I set up Windows Network Virtualization
# Enable the WNV binding
# At my local host
$vSwitch = Get-VMSwitch -SwitchType External
Enable-NetAdapterBinding -InterfaceDescription $vSwitch.NetAdapterInterfaceDescription -ComponentID "ms_netwnv"
# At my Remote host
Invoke-Command -Session $waldorf {
$vSwitch = Get-VMSwitch -SwitchType External
Enable-NetAdapterBinding -InterfaceDescription $vSwitch.NetAdapterInterfaceDescription -ComponentID "ms_netwnv"
}
# Define the Provider Address in my address space
# At my local host
$vSwitch = Get-VMSwitch -SwitchType External
$swPhysIf = Get-NetAdapter -InterfaceDescription $vSwitch.NetAdapterInterfaceDescription
New-NetVirtualizationProviderAddress -InterfaceIndex $swPhysIf.InterfaceIndex -ProviderAddress 172.16.0.1 -PrefixLength 21
# At my Remote host
Invoke-Command -Session $waldorf {
$vSwitch = Get-VMSwitch -SwitchType External
$swPhysIf = Get-NetAdapter -InterfaceDescription $vSwitch.NetAdapterInterfaceDescription
New-NetVirtualizationProviderAddress -InterfaceIndex $swPhysIf.InterfaceIndex -ProviderAddress 172.16.0.2 -PrefixLength 21
}
# Generate a GUID for each customer
$redGUID = [system.guid]::newguid()
$blueGUID = [system.guid]::newguid()
# Format the GUID string properly
$redGUID = "{" + [string]$redGUID + "}"
$blueGUID = "{" + [string]$blueGUID + "}"
# Define a Customer Route for each customer
# A the local host
New-NetVirtualizationCustomerRoute -RoutingDomainID $redGUID -VirtualSubnetID 445566 -DestinationPrefix "192.168.1.0/24“ -NextHop 0.0.0.0 -Metric 255
New-NetVirtualizationCustomerRoute -RoutingDomainID $blueGUID -VirtualSubnetID 7788990 -DestinationPrefix "192.168.1.0/24“ -NextHop 0.0.0.0 -Metric 255
# At the remote host
Invoke-Command -Session $waldorf -ArgumentList $redGUID, $blueGUID {
Param($redGUID, $blueGUID)
New-NetVirtualizationCustomerRoute -RoutingDomainID $redGUID -VirtualSubnetID 445566 -DestinationPrefix "192.168.1.0/24“ -NextHop 0.0.0.0 -Metric 255
New-NetVirtualizationCustomerRoute -RoutingDomainID $blueGUID -VirtualSubnetID 7788990 -DestinationPrefix "192.168.1.0/24“ -NextHop 0.0.0.0 -Metric 255
}
# Define the Lookup Routes on all switches that require it
# First local
New-NetVirtualizationLookupRecord -VMName Red11 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.11 -MACAddress 00155D002009 -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $redGUID
New-NetVirtualizationLookupRecord -VMName Red22 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.22 -MACAddress 00155D002008 -ProviderAddress 172.16.0.2 -Rule TranslationMethodEncap -CustomerID $redGUID
New-NetVirtualizationLookupRecord -VMName Blue11 -VirtualSubnetID 7788990 -CustomerAddress 192.168.1.11 -MACAddress 00155D00200B -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $blueGUID
New-NetVirtualizationLookupRecord -VMName Blue22 -VirtualSubnetID 7788990 -CustomerAddress 192.168.1.22 -MACAddress 00155D00200A -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $blueGUID
# Then remote
Invoke-Command -Session $waldorf -ArgumentList $redGUID, $blueGUID {
Param($redGUID, $blueGUID)
New-NetVirtualizationLookupRecord -VMName Red11 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.11 -MACAddress 00155D002009 -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $redGUID
New-NetVirtualizationLookupRecord -VMName Red22 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.22 -MACAddress 00155D002008 -ProviderAddress 172.16.0.2 -Rule TranslationMethodEncap -CustomerID $redGUID
New-NetVirtualizationLookupRecord -VMName Blue11 -VirtualSubnetID 7788990 -CustomerAddress 192.168.1.11 -MACAddress 00155D00200B -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $blueGUID
New-NetVirtualizationLookupRecord -VMName Blue22 -VirtualSubnetID 7788990 -CustomerAddress 192.168.1.22 -MACAddress 00155D00200A -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $blueGUID
}
# Ping is working again
# Take a look and the LookupRoutes
Get-NetVirtualizationLookupRecord | ft CustomerAddress, CustomerID, Provideraddress, macaddress, rule, vmname -AutoSize
# Break one of the routes, this can be due to a VM HA event, or a mis-configuraiton.
Remove-NetVirtualizationLookupRecord -CustomerAddress 192.168.1.22 -VMName Red22
New-NetVirtualizationLookupRecord -VMName Red22 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.22 -MACAddress 00155D002008 -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $redGUID
# Now the route is incorrect, what happens? (check ping)
# Repair the route Remove-NetVirtualizationLookupRecord -CustomerAddress 192.168.1.22 -VMName Red22
New-NetVirtualizationLookupRecord -VMName Red22 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.22 -MACAddress 00155D002008 -ProviderAddress 172.16.0.2 -Rule TranslationMethodEncap -CustomerID $redGUID