diff --git a/.gitignore b/.gitignore index becdefa..186dc98 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ website/vendor /.vs /.idea /.terraform + +# Ignore output binary +terraform-provider-hyperv \ No newline at end of file diff --git a/api/hyperv-winrm/vm_switch.go b/api/hyperv-winrm/vm_switch.go index 40a6dc6..259aee4 100644 --- a/api/hyperv-winrm/vm_switch.go +++ b/api/hyperv-winrm/vm_switch.go @@ -90,6 +90,16 @@ $SetVmSwitchArgs.DefaultQueueVrssEnabled=$vmSwitch.DefaultQueueVrssEnabled Set-VMSwitch @SetVmSwitchArgs +if ($vmSwitch.OperationMode -eq 1) { + $SetVmNetworkAdapterVlanArgs = @{} + $SetVmNetworkAdapterVlanArgs.VMNetworkAdapterName=$vmSwitch.Name + $SetVmNetworkAdapterVlanArgs.ManagementOS=$true + $SetVmNetworkAdapterVlanArgs.Access=$true + $SetVmNetworkAdapterVlanArgs.VlanID=$vmSwitch.VlanId + + Set-VMNetworkAdapterVlan @SetVmNetworkAdapterVlanArgs +} + `)) func (c *ClientConfig) CreateVMSwitch( @@ -108,6 +118,8 @@ func (c *ClientConfig) CreateVMSwitch( defaultQueueVmmqEnabled bool, defaultQueueVmmqQueuePairs int32, defaultQueueVrssEnabled bool, + operationMode api.VMSwitchOperationMode, + vlanId int, ) (err error) { vmSwitchJson, err := json.Marshal(api.VmSwitch{ Name: name, @@ -124,6 +136,8 @@ func (c *ClientConfig) CreateVMSwitch( DefaultQueueVmmqEnabled: defaultQueueVmmqEnabled, DefaultQueueVmmqQueuePairs: defaultQueueVmmqQueuePairs, DefaultQueueVrssEnabled: defaultQueueVrssEnabled, + OperationMode: operationMode, + VlanID: vlanId, }) if err != nil { @@ -143,6 +157,11 @@ type getVMSwitchArgs struct { var getVMSwitchTemplate = template.Must(template.New("GetVMSwitch").Parse(` $ErrorActionPreference = 'Stop' +$vmAdapterVlanObject = Get-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName '{{.Name}}*' | %{ @{ + OperationMode=$_.OperationMode + AccessVlanId=$_.AccessVlanId +}} + $vmSwitchObject = Get-VMSwitch -Name '{{.Name}}*' | ?{$_.Name -eq '{{.Name}}' } | %{ @{ Name=$_.Name; Notes=$_.Notes; @@ -158,8 +177,11 @@ $vmSwitchObject = Get-VMSwitch -Name '{{.Name}}*' | ?{$_.Name -eq '{{.Name}}' } DefaultQueueVmmqEnabled=$_.DefaultQueueVmmqEnabledRequested; DefaultQueueVmmqQueuePairs=$_.DefaultQueueVmmqQueuePairsRequested; DefaultQueueVrssEnabled=$_.DefaultQueueVrssEnabledRequested; + OperationMode=$vmAdapterVlanObject.OperationMode + VlanID=$vmAdapterVlanObject.AccessVlanId }} + if ($vmSwitchObject){ $vmSwitch = ConvertTo-Json -InputObject $vmSwitchObject $vmSwitch @@ -207,7 +229,8 @@ $SetVmSwitchArgs.Name=$vmSwitch.Name $SetVmSwitchArgs.Notes=$vmSwitch.Notes if ($NetAdapterNames) { $SetVmSwitchArgs.AllowManagementOS=$vmSwitch.AllowManagementOS - $SetVmSwitchArgs.NetAdapterName=$NetAdapterNames + # Converts the incoming Object[] to a String as expected by the command + $SetVmSwitchArgs.NetAdapterName=[system.String]::Join(",", $NetAdapterNames) #Updates not supported on: #-EnableEmbeddedTeaming $vmSwitch.EmbeddedTeamingEnabled #-EnableIov $vmSwitch.IovEnabled @@ -236,6 +259,16 @@ $SetVmSwitchArgs.DefaultQueueVmmqQueuePairs=$vmSwitch.DefaultQueueVmmqQueuePairs $SetVmSwitchArgs.DefaultQueueVrssEnabled=$vmSwitch.DefaultQueueVrssEnabled Set-VMSwitch @SetVmSwitchArgs + +if ($vmSwitch.OperationMode -eq 1) { + $SetVmNetworkAdapterVlanArgs = @{} + $SetVmNetworkAdapterVlanArgs.VMNetworkAdapterName=$vmSwitch.Name + $SetVmNetworkAdapterVlanArgs.ManagementOS=$true + $SetVmNetworkAdapterVlanArgs.Access=$true + $SetVmNetworkAdapterVlanArgs.VlanID=$vmSwitch.VlanId + + Set-VMNetworkAdapterVlan @SetVmNetworkAdapterVlanArgs +} `)) func (c *ClientConfig) UpdateVMSwitch( @@ -255,6 +288,8 @@ func (c *ClientConfig) UpdateVMSwitch( defaultQueueVmmqEnabled bool, defaultQueueVmmqQueuePairs int32, defaultQueueVrssEnabled bool, + operationMode api.VMSwitchOperationMode, + vlanId int, ) (err error) { vmSwitchJson, err := json.Marshal(api.VmSwitch{ Name: name, @@ -271,6 +306,8 @@ func (c *ClientConfig) UpdateVMSwitch( DefaultQueueVmmqEnabled: defaultQueueVmmqEnabled, DefaultQueueVmmqQueuePairs: defaultQueueVmmqQueuePairs, DefaultQueueVrssEnabled: defaultQueueVrssEnabled, + OperationMode: operationMode, + VlanID: vlanId, }) if err != nil { diff --git a/api/vm_switch.go b/api/vm_switch.go index ada356f..e5085a1 100644 --- a/api/vm_switch.go +++ b/api/vm_switch.go @@ -8,6 +8,65 @@ import ( "strings" ) +type VMSwitchOperationMode int + +const ( + VMSwitchOperationMode_Untagged VMSwitchOperationMode = 0 + VMSwitchOperationMode_Isolated VMSwitchOperationMode = 1 + VMSwitchOperationMode_Community VMSwitchOperationMode = 2 + VMSwitchOperationMode_Promiscuous VMSwitchOperationMode = 3 +) + +var VMSwitchOperationMode_name = map[VMSwitchOperationMode]string{ + VMSwitchOperationMode_Untagged: "Untagged", + VMSwitchOperationMode_Isolated: "Isolated", + VMSwitchOperationMode_Community: "Community", + VMSwitchOperationMode_Promiscuous: "Promiscuous", +} + +var VMSwitchOperationMode_value = map[string]VMSwitchOperationMode{ + "untagged": VMSwitchOperationMode_Untagged, + "isolated": VMSwitchOperationMode_Isolated, + "community": VMSwitchOperationMode_Community, + "promiscuous": VMSwitchOperationMode_Promiscuous, +} + +func (x VMSwitchOperationMode) String() string { + return VMSwitchOperationMode_name[x] +} + +func ToVMSwitchOperationMode(x string) VMSwitchOperationMode { + if integerValue, err := strconv.Atoi(x); err == nil { + return VMSwitchOperationMode(integerValue) + } + + return VMSwitchOperationMode_value[strings.ToLower(x)] +} + +func (d *VMSwitchOperationMode) MarshalJSON() ([]byte, error) { + buffer := bytes.NewBufferString(`"`) + buffer.WriteString(d.String()) + buffer.WriteString(`"`) + return buffer.Bytes(), nil +} + +func (d *VMSwitchOperationMode) UnmarshalJSON(b []byte) error { + var s string + err := json.Unmarshal(b, &s) + if err != nil { + var i int + err2 := json.Unmarshal(b, &i) + if err2 == nil { + *d = VMSwitchOperationMode(i) + return nil + } + + return err + } + *d = ToVMSwitchOperationMode(s) + return nil +} + type VMSwitchBandwidthMode int const ( @@ -142,6 +201,8 @@ type VmSwitch struct { DefaultQueueVmmqEnabled bool DefaultQueueVmmqQueuePairs int32 DefaultQueueVrssEnabled bool + OperationMode VMSwitchOperationMode + VlanID int } type HypervVmSwitchClient interface { @@ -162,6 +223,8 @@ type HypervVmSwitchClient interface { defaultQueueVmmqEnabled bool, defaultQueueVmmqQueuePairs int32, defaultQueueVrssEnabled bool, + vlanOperationMode VMSwitchOperationMode, + vlanId int, ) (err error) GetVMSwitch(ctx context.Context, name string) (result VmSwitch, err error) UpdateVMSwitch( @@ -181,6 +244,8 @@ type HypervVmSwitchClient interface { defaultQueueVmmqEnabled bool, defaultQueueVmmqQueuePairs int32, defaultQueueVrssEnabled bool, + vlanOperationMode VMSwitchOperationMode, + vlanId int, ) (err error) DeleteVMSwitch(ctx context.Context, name string) (err error) } diff --git a/api/vm_switch_test.go b/api/vm_switch_test.go index 872d41d..be92ecd 100644 --- a/api/vm_switch_test.go +++ b/api/vm_switch_test.go @@ -18,6 +18,8 @@ func TestSerializeVmSwitch(t *testing.T) { NetAdapterNames: []string{"wan", "lan"}, DefaultQueueVrssEnabled: true, DefaultQueueVmmqQueuePairs: 0, + OperationMode: VMSwitchOperationMode_Isolated, + VlanID: 42, }) if err != nil { @@ -44,7 +46,9 @@ func TestDeserializeVmSwitch(t *testing.T) { "SwitchType": 2, "IovEnabled": false, "EmbeddedTeamingEnabled": false, - "PacketDirectEnabled": false + "PacketDirectEnabled": false, + "OperationMode": 1, + "VlanID": 42 } ` diff --git a/docs/resources/network_switch.md b/docs/resources/network_switch.md index a188ad7..717a7a8 100644 --- a/docs/resources/network_switch.md +++ b/docs/resources/network_switch.md @@ -66,6 +66,8 @@ resource "hyperv_network_switch" "default" { - `notes` (String) Specifies a note to be associated with the switch to be created. - `switch_type` (String) Specifies the type of the switch to be created. Valid values to use are `Internal`, `Private` and `External`. - `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) +- `vlan_access_id` (Number) The VLAN identifier used for all network communications through this network adapter. This setting does not affect virtual machine networking +- `vlan_operation_mode` (String) Operation Mode for the management operating system. Valid values are [Untagged, Isolated, Community, Promiscuous]. Only Untagged and Isolated are currently supported ### Read-Only diff --git a/internal/provider/resource_hyperv_network_switch.go b/internal/provider/resource_hyperv_network_switch.go index 48b6f84..e468488 100644 --- a/internal/provider/resource_hyperv_network_switch.go +++ b/internal/provider/resource_hyperv_network_switch.go @@ -139,6 +139,21 @@ func resourceHyperVNetworkSwitch() *schema.Resource { Default: false, Description: "Should Virtual Receive Side Scaling be enabled. This configuration allows the load from a virtual network adapter to be distributed across multiple virtual processors in a virtual machine (VM), allowing the VM to process more network traffic more rapidly than it can with a single logical processor.", }, + + "vlan_operation_mode": { + Type: schema.TypeString, + Optional: true, + Default: api.VMSwitchOperationMode_name[api.VMSwitchOperationMode_Untagged], + ValidateDiagFunc: StringKeyInMap(api.VMSwitchOperationMode_value, true), + Description: "Operation Mode for the management operating system. Valid values are [Untagged, Isolated, Community, Promiscuous]. Only Untagged and Isolated are currently supported", + }, + + "vlan_access_id": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + Description: "The VLAN identifier used for all network communications through this network adapter. This setting does not affect virtual machine networking", + }, }, } } @@ -236,7 +251,10 @@ func resourceHyperVNetworkSwitchCreate(ctx context.Context, d *schema.ResourceDa return diag.Errorf("[ERROR][hyperv][create] defaultQueueVmmqQueuePairs must be greater then 0") } - err := c.CreateVMSwitch(ctx, switchName, notes, allowManagementOS, embeddedTeamingEnabled, iovEnabled, packetDirectEnabled, bandwidthReservationMode, switchType, netAdapterNames, defaultFlowMinimumBandwidthAbsolute, defaultFlowMinimumBandwidthWeight, defaultQueueVmmqEnabled, defaultQueueVmmqQueuePairs, defaultQueueVrssEnabled) + vlanOperationMode := api.ToVMSwitchOperationMode((d.Get("vlan_operation_mode")).(string)) + vlanAccessId := int((d.Get("vlan_access_id")).(int)) + + err := c.CreateVMSwitch(ctx, switchName, notes, allowManagementOS, embeddedTeamingEnabled, iovEnabled, packetDirectEnabled, bandwidthReservationMode, switchType, netAdapterNames, defaultFlowMinimumBandwidthAbsolute, defaultFlowMinimumBandwidthWeight, defaultQueueVmmqEnabled, defaultQueueVmmqQueuePairs, defaultQueueVrssEnabled, vlanOperationMode, vlanAccessId) if err != nil { return diag.FromErr(err) @@ -363,6 +381,12 @@ func resourceHyperVNetworkSwitchRead(ctx context.Context, d *schema.ResourceData if err := d.Set("default_queue_vrss_enabled", s.DefaultQueueVrssEnabled); err != nil { return diag.FromErr(err) } + if err := d.Set("vlan_operation_mode", s.OperationMode.String()); err != nil { + return diag.FromErr(err) + } + if err := d.Set("vlan_access_id", s.VlanID); err != nil { + return diag.FromErr(err) + } log.Printf("[INFO][hyperv][read] read hyperv switch: %#v", d) @@ -446,7 +470,10 @@ func resourceHyperVNetworkSwitchUpdate(ctx context.Context, d *schema.ResourceDa return diag.Errorf("[ERROR][hyperv][update] defaultQueueVmmqQueuePairs must be greater then 0") } - err := c.UpdateVMSwitch(ctx, id, newName, notes, allowManagementOS, switchType, netAdapterNames, defaultFlowMinimumBandwidthAbsolute, defaultFlowMinimumBandwidthWeight, defaultQueueVmmqEnabled, defaultQueueVmmqQueuePairs, defaultQueueVrssEnabled) + vlanOperationMode := api.ToVMSwitchOperationMode((d.Get("vlan_operation_mode")).(string)) + vlanAccessId := int((d.Get("vlan_access_id")).(int)) + + err := c.UpdateVMSwitch(ctx, id, newName, notes, allowManagementOS, switchType, netAdapterNames, defaultFlowMinimumBandwidthAbsolute, defaultFlowMinimumBandwidthWeight, defaultQueueVmmqEnabled, defaultQueueVmmqQueuePairs, defaultQueueVrssEnabled, vlanOperationMode, vlanAccessId) if err != nil { return diag.FromErr(err)