Disclaimer: This is certainly not a supported/recommended way of doing this. Don't do it on a production box. I did it with all VMs powered off, and vSphere disconnected, just to be safe.
First, you need to access the ESXi console. Then, this can easily be done by editing /etc/vmware/esx.conf.
For each vSwitch, there is an entry like:
/net/vswitch/child[0000]/name = "vSwitch0"
/net/vswitch/child[0000]/...
/net/vswitch/child[0001]/name = "vSwitch1"
/net/vswitch/child[0001]/...
...
You can rename vSwitches there, by the way.
The port groups are nodes under the vSwitches:
/net/vswitch/child[0000]/portgroup/child[0000]/name = "Management Network"
/net/vswitch/child[0000]/portgroup/child[0000]/...
...
/net/vswitch/child[0000]/portgroup/child[0001]/name = "VM Network"
/net/vswitch/child[0000]/portgroup/child[0001]/...
...
To move a port group from one vSwitch to another, you simply change which /net/vswitch/child[XXXX]/ node it is under, making sure to respect what portgroup children are already there. In other words, once you move a port group to another vSwitch, you might need to change its child number.
Note: You have to re-number all of the relevant lines (all of the lines that correspond to that port group).
Note 2: If there are teamPolicy entries present for a port group, you may need to ensure that the associated uplinks[nnnn]/pnic = settings match the actual vmnicN for the vswitch that you're moving it to.
After editing the file, I simply rebooted the ESXi server: $ reboot. There may be some service or process that could be restarted instead, but I figured rebooting was the easiest and safest way.
Example
Say we have two vSwitches each with a single vmnic uplink, and two port groups:
vSwitch0 --> vmnic0
|--portGroupA
|--portGroupB
vSwitch1 --> vmnic1
|--portGroupC
|--portGroupD
The relevant entries in esx.conf might look something like this1:
# vSwitch0
/net/vswitch/child[0000]/...
/net/vswitch/child[0000]/name = "vSwitch0"
/net/vswitch/child[0000]/numPorts = "128"
# vSwitch0->portGroupA
/net/vswitch/child[0000]/portgroup/child[0000]/name = "portGroupA"
/net/vswitch/child[0000]/portgroup/child[0000]/teamPolicy/uplinks[0000]/pnic = "vmnic0"
# vSwitch0->portGroupB
/net/vswitch/child[0000]/portgroup/child[0001]/name = "portGroupB"
/net/vswitch/child[0000]/portgroup/child[0001]/teamPolicy/uplinks[0000]/pnic = "vmnic0"
/net/vswitch/child[0000]/uplinks/child[0000]/pnic = "vmnic0"
# vSwitch1
/net/vswitch/child[0001]/...
/net/vswitch/child[0001]/name = "vSwitch1"
/net/vswitch/child[0001]/numPorts = "128"
# vSwitch1->portGroupC
/net/vswitch/child[0001]/portgroup/child[0000]/name = "portGroupC"
/net/vswitch/child[0001]/portgroup/child[0000]/teamPolicy/uplinks[0000]/pnic = "vmnic1"
# vSwitch1->portGroupD
/net/vswitch/child[0001]/portgroup/child[0001]/name = "portGroupD"
/net/vswitch/child[0001]/portgroup/child[0001]/teamPolicy/uplinks[0000]/pnic = "vmnic1"
/net/vswitch/child[0001]/uplinks/child[0000]/pnic = "vmnic1"
Now, we want to move portGroupC from vSwitch1 to vSwitch0, so the configuration is like this:
vSwitch0 --> vmnic0
|--portGroupA
|--portGroupB
|--portGroupC
vSwitch1 --> vmnic1
|--portGroupD
To do this, we:
- Identify all of the
portGroupC lines, which start with /net/vswitch/child[0001]/portgroup/child[0000]. Move those entries up with the vSwitch0 config (not necessary, but makes things clearer when editing).
- Change
/net/vswitch/child[0001] to /net/vswitch/child[0000] on each line (because we're moving it to that switch).
- Realize that there is already a
/net/vswitch/child[0000]/portgroup/child[0000] (portGroupA), and change portGroupC to /portgroup/child[0002].
- Realize that our uplink for that port group is now incorrect (if specified), and change
uplinks[0000]/pnic = from vmnic1 to vmnic0 (because that is the vmnic serving that vSwitch.)
The final config should look like this:
# vSwitch0
/net/vswitch/child[0000]/...
/net/vswitch/child[0000]/name = "vSwitch0"
/net/vswitch/child[0000]/numPorts = "128"
# vSwitch0->portGroupA
/net/vswitch/child[0000]/portgroup/child[0000]/name = "portGroupA"
/net/vswitch/child[0000]/portgroup/child[0000]/teamPolicy/uplinks[0000]/pnic = "vmnic0"
# vSwitch0->portGroupB
/net/vswitch/child[0000]/portgroup/child[0001]/name = "portGroupB"
/net/vswitch/child[0000]/portgroup/child[0001]/teamPolicy/uplinks[0000]/pnic = "vmnic0"
# vSwitch1->portGroupC
/net/vswitch/child[0000]/portgroup/child[0002]/name = "portGroupC"
/net/vswitch/child[0000]/portgroup/child[0002]/teamPolicy/uplinks[0000]/pnic = "vmnic0"
/net/vswitch/child[0000]/uplinks/child[0000]/pnic = "vmnic0"
# vSwitch1
/net/vswitch/child[0001]/...
/net/vswitch/child[0001]/name = "vSwitch1"
/net/vswitch/child[0001]/numPorts = "128"
# vSwitch1->portGroupD
/net/vswitch/child[0001]/portgroup/child[0000]/name = "portGroupD"
/net/vswitch/child[0001]/portgroup/child[0000]/teamPolicy/uplinks[0000]/pnic = "vmnic1"
/net/vswitch/child[0001]/uplinks/child[0000]/pnic = "vmnic1"
Notice that I also changed portGroupD from portgroup/child[0001] to [0000] (as there was no longer a 0 entry.)
1 - The # comments are just for explanation here; I don't know if they work in the real file, and get blown away upon next configuration anyway.