Skip to content

Commit

Permalink
Merge pull request #1008 from ninjarobot/ip-forwarding-accel-net
Browse files Browse the repository at this point in the history
VM support for IP forwarding and accelerated networking.
  • Loading branch information
ninjarobot authored Feb 10, 2023
2 parents a0cebea + 560fcdf commit 3b41f78
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 3 deletions.
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Release Notes
* Container Apps: Support for collections of env vars, fix ACR credentials linking.
* Container Apps: Add Dapr / App Insights integration.

## vNext
* Virtual Machines: Accelerated networking and IP forwarding for interfaces

## 1.7.15
* Adds the West US 3 location
* Virtual Machines: Support for multiple network interfaces on a VM
Expand Down
8 changes: 5 additions & 3 deletions docs/content/api-overview/resources/virtual-machine.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,16 @@ In addition, every VM you create will add a SecureString parameter to the ARM te
|custom_script_files|Uploads the supplied set of files, specified by URI, to the VM on creation.|
|aad_ssh_login|Adds the `AADSSHLoginForLinux` extension on Linux VM's (requires `system_identity`).|
|custom_data|Sets the custom data field for the VM.|
|public_ip|Specifies or removes the public IP for this VM|
|ip_allocation|Sets the public IP as Dynamic or Static. Default is Dynamic.|
|disable_password_authentication|Disables password authentication on the VM. Must include at least one key if true|
|add_authorized_key|adds one authorized key|
|add_authorized_keys|adds a list of authorized keys|
|add_identity|Adds a managed identity to the Virtual Machine.|
|system_identity|Activates the system identity of the Virtual Machine.|
|private_ip_allocation| Sets the private ip as Dynamic or Static default is dynamic.|
|public_ip|Specifies or removes the public IP for this VM|
|ip_allocation|Sets the *public* IP as Dynamic or Static. Default is dynamic.|
|private_ip_allocation| Sets the *private* IP as Dynamic or Static. Default is dynamic.|
|ip_forwarding|Enable or disable IP forwarding on the primary network interface. Secondary NICs will leave it undefined.|
|accelerated_networking|Enable or disable accelerated networking on all network interfaces generated for the VM.|
|add_ip_configuration| Add `ipConfig` definitions to add additional IP addresses or connect to multiple subnets. Connecting to additional subnets will generate a NIC for each subnet. |
|network_security_group| Sets the Network Security Group (NSG) for VM/NIC. Enables you to create and share firewall rule sets.|
|link_to_network_security_group| Specify an existing Network Security Group (NSG) for VM/NIC. |
Expand Down
23 changes: 23 additions & 0 deletions src/Farmer/Arm/Network.fs
Original file line number Diff line number Diff line change
Expand Up @@ -558,10 +558,31 @@ type IpConfiguration =
Primary: bool option
}

module NetworkInterface =
open Vm

/// Accelerated networking only supported on certain VM sizes.
let (|AcceleratedNetworkingSupported|AcceleratedNetworkingUnsupported|) (vmSize: VMSize) =
let size = vmSize.ArmValue

if size.Contains "_A" || size.Contains "_NC" || size.Contains "_NV" then
AcceleratedNetworkingUnsupported
else
match vmSize with
| Standard_B1ls
| Standard_B1ms
| Standard_B1s
| Standard_B2s
| Standard_B4ms
| Standard_B8ms -> AcceleratedNetworkingUnsupported // failwithf "Accelerated networking unsupported for specified VM size. Using '%s'." state.Size.ArmValue
| _ -> AcceleratedNetworkingSupported

type NetworkInterface =
{
Name: ResourceName
Location: Location
EnableAcceleratedNetworking: bool option
EnableIpForwarding: bool option
IpConfigs: IpConfiguration list
VirtualNetwork: LinkedResource
NetworkSecurityGroup: ResourceId option
Expand Down Expand Up @@ -594,6 +615,8 @@ type NetworkInterface =
let props =
{|
primary = this.Primary |> Option.map box |> Option.toObj
enableAcceleratedNetworking = this.EnableAcceleratedNetworking |> Option.map box |> Option.toObj
enableIPForwarding = this.EnableIpForwarding |> Option.map box |> Option.toObj
ipConfigurations =
this.IpConfigs
|> List.mapi (fun index ipConfig ->
Expand Down
30 changes: 30 additions & 0 deletions src/Farmer/Builders/Builders.Vm.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Farmer.Builders.VirtualMachine

open Farmer
open Farmer.Arm
open Farmer.FeatureFlag
open Farmer.PublicIpAddress
open Farmer.PrivateIpAddress
open Farmer.Vm
Expand Down Expand Up @@ -48,6 +49,8 @@ type VmConfig =
Subnet: AutoGeneratedResource<VmConfig>
PublicIp: ResourceRef<VmConfig> option
IpAllocation: PublicIpAddress.AllocationMethod option
AcceleratedNetworking: FeatureFlag option
IpForwarding: FeatureFlag option
IpConfigs: IpConfiguration list
PrivateIpAllocation: PrivateIpAddress.AllocationMethod option
LoadBalancerBackendAddressPools: LinkedResource list
Expand Down Expand Up @@ -116,6 +119,12 @@ type VmConfig =
else
ResourceName $"{this.NicName.Name.Value}-{subnetName.Value}"
Location = location
EnableAcceleratedNetworking = this.AcceleratedNetworking |> Option.map toBool
EnableIpForwarding = // IP forwarding optionally enabled on primary NIC only.
if isPrimaryNic then
this.IpForwarding |> Option.map toBool
else
None
IpConfigs = subnetIpConfigs
Primary =
if ipConfigsBySubnet.Length > 1 then // multiple NICs, need to indicate the primary
Expand Down Expand Up @@ -314,6 +323,8 @@ type VirtualMachineBuilder() =
Subnet = Derived(fun config -> config.DeriveResourceName subnets "subnet")
PublicIp = automaticPublicIp
IpAllocation = None
AcceleratedNetworking = None
IpForwarding = None
IpConfigs = []
PrivateIpAllocation = None
LoadBalancerBackendAddressPools = []
Expand All @@ -322,6 +333,14 @@ type VirtualMachineBuilder() =
}

member _.Run(state: VmConfig) =
match state.AcceleratedNetworking with
| Some (Enabled) ->
match state.Size with
| NetworkInterface.AcceleratedNetworkingUnsupported ->
raiseFarmer $"Accelerated networking unsupported for specified VM size '{state.Size.ArmValue}'."
| NetworkInterface.AcceleratedNetworkingSupported -> ()
| _ -> ()

{ state with
DataDisks =
state.DataDisks
Expand Down Expand Up @@ -492,6 +511,17 @@ type VirtualMachineBuilder() =
member this.SubnetName(state: VmConfig, name) =
this.SubnetName(state, ResourceName name)

/// Control accelerated networking for the VM network interfaces
[<CustomOperation "accelerated_networking">]
member _.AcceleratedNetworking(state: VmConfig, flag: FeatureFlag) =
{ state with
AcceleratedNetworking = Some flag
}

/// Enable or disable IP forwarding on the primary VM network interface.
[<CustomOperation "ip_forwarding">]
member _.IpForwarding(state: VmConfig, flag: FeatureFlag) = { state with IpForwarding = Some flag }

/// Uses an external VNet instead of creating a new one.
[<CustomOperation "link_to_vnet">]
member _.LinkToVNet(state: VmConfig, name: ResourceName) =
Expand Down
1 change: 1 addition & 0 deletions src/Farmer/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ type FeatureFlag =

module FeatureFlag =
let ofBool enabled = if enabled then Enabled else Disabled
let toBool (flag: FeatureFlag) = flag.AsBoolean

let invert flag =
match flag with
Expand Down
85 changes: 85 additions & 0 deletions src/Tests/VirtualMachine.fs
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,91 @@ let tests =
Expect.equal (secondNicIp.ToString()) "192.168.12.13" "Static IP is wrong or missing"
}

test "IP forwarding set for first NIC only" {
let deployment =
arm {
add_resources
[
vm {
name "foo"
username "foo"
ip_forwarding Enabled

add_ip_configurations [ ipConfig { subnet_name (ResourceName "another-subnet") } ]
}
]
}

let jobj = Newtonsoft.Json.Linq.JObject.Parse(deployment.Template |> Writer.toJson)
let firstNicProps = jobj.SelectToken("resources[?(@.name=='foo-nic')].properties")

Expect.equal
firstNicProps.["enableIPForwarding"]
(JValue true)
"First NIC should have IP forwarding enabled"

let secondNicProps =
jobj.SelectToken("resources[?(@.name=='foo-nic-another-subnet')].properties")

Expect.isNull secondNicProps.["enableIPForwarding"] "Second NIC should not have IP forwarding"
}

test "Accelerated networking set for all NICs" {
let deployment =
arm {
add_resources
[
vm {
name "foo"
username "foo"
vm_size Standard_D2s_v5
accelerated_networking Enabled

add_ip_configurations [ ipConfig { subnet_name (ResourceName "another-subnet") } ]
}
]
}

let jobj = Newtonsoft.Json.Linq.JObject.Parse(deployment.Template |> Writer.toJson)
let firstNicProps = jobj.SelectToken("resources[?(@.name=='foo-nic')].properties")

Expect.equal
firstNicProps.["enableAcceleratedNetworking"]
(JValue true)
"First NIC should have accelerated networking enabled"

let secondNicProps =
jobj.SelectToken("resources[?(@.name=='foo-nic-another-subnet')].properties")

Expect.equal
secondNicProps.["enableAcceleratedNetworking"]
(JValue true)
"Second NIC should have accelerated networking enabled"
}

test "Accelerated networking not allowed on A-series VM" {
Expect.throws
(fun _ ->
let _ =
arm {
add_resources
[
vm {
name "foo"
username "foo"
vm_size Basic_A0
accelerated_networking Enabled

add_ip_configurations
[ ipConfig { subnet_name (ResourceName "another-subnet") } ]
}
]
}

())
"Expected failure using accelerated networking with default VM size."
}

test "Can attach to NSG" {
let vmName = "fooVm"
let myNsg = nsg { name "testNsg" }
Expand Down

0 comments on commit 3b41f78

Please sign in to comment.