diff --git a/tests/bundle.at b/tests/bundle.at
index 0bc179f..11c9713 100644
--- a/tests/bundle.at
+++ b/tests/bundle.at
@@ -8,6 +8,7 @@ AT_BANNER([bundle link selection])
 # if the test does fail.
 
 AT_SETUP([hrw bundle link selection])
+AT_KEYWORDS([bundle_action])
 AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],slaves:1,2,3,4,5']],
   [0], [ignore])
 # 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
@@ -78,6 +79,7 @@ AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],slaves:
 AT_CLEANUP
 
 AT_SETUP([active_backup bundle link selection])
+AT_KEYWORDS([bundle_action])
 AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,active_backup,ofport,NXM_NX_REG0[],slaves:1,2,3,4,5,6']],
   [0],
 [100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
@@ -149,6 +151,7 @@ AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,active_backup,ofport,NXM_NX_REG0
 AT_CLEANUP
 
 AT_SETUP([hrw bundle single link selection])
+AT_KEYWORDS([bundle_action])
 AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],slaves:1']],
   [0], [ignore])
 # 1: disruption=1.00 (perfect=1.00) 1.00
@@ -157,6 +160,7 @@ AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],slaves:
 AT_CLEANUP
 
 AT_SETUP([hrw bundle no link selection])
+AT_KEYWORDS([bundle_action])
 AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],slaves:']],
   [0], [ignore])
 AT_CLEANUP
@@ -164,30 +168,35 @@ AT_CLEANUP
 #: disruption=0.00 (perfect=0.00)
 
 AT_SETUP([bundle action missing argument])
+AT_KEYWORDS([bundle_action])
 AT_CHECK([ovs-ofctl parse-flow actions=bundle], [1], [],
   [ovs-ofctl: : not enough arguments to bundle action
 ])
 AT_CLEANUP
 
 AT_SETUP([bundle action bad fields])
+AT_KEYWORDS([bundle_action])
 AT_CHECK([ovs-ofctl parse-flow 'actions=bundle(xyzzy,60,hrw,ofport,slaves:1,2))'], [1], [],
   [ovs-ofctl: xyzzy,60,hrw,ofport,slaves:1,2: unknown fields `xyzzy'
 ])
 AT_CLEANUP
 
 AT_SETUP([bundle action bad algorithm])
+AT_KEYWORDS([bundle_action])
 AT_CHECK([ovs-ofctl parse-flow 'actions=bundle(symmetric_l4,60,fubar,ofport,slaves:1,2))'], [1], [],
   [ovs-ofctl: symmetric_l4,60,fubar,ofport,slaves:1,2: unknown algorithm `fubar'
 ])
 AT_CLEANUP
 
 AT_SETUP([bundle action bad slave type])
+AT_KEYWORDS([bundle_action])
 AT_CHECK([ovs-ofctl parse-flow 'actions=bundle(symmetric_l4,60,hrw,robot,slaves:1,2))'], [1], [],
   [ovs-ofctl: symmetric_l4,60,hrw,robot,slaves:1,2: unknown slave_type `robot'
 ])
 AT_CLEANUP
 
 AT_SETUP([bundle action bad slave delimiter])
+AT_KEYWORDS([bundle_action])
 AT_CHECK([ovs-ofctl parse-flow 'actions=bundle(symmetric_l4,60,hrw,ofport,robot:1,2))'], [1], [],
   [ovs-ofctl: symmetric_l4,60,hrw,ofport,robot:1,2: missing slave delimiter, expected `slaves' got `robot'
 ])
@@ -200,6 +209,7 @@ dnl are used, the encode/decode still works correctly. By placing the bundle
 dnl action deep within a list of actions, this test was able to trigger
 dnl Valgrind warnings for use-after-free bugs.
 AT_SETUP([bundle action with many ports])
+AT_KEYWORDS([bundle_action])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 'actions=set_field:0x1->metadata,set_field:0x2->metadata,set_field:0x3->metadata,set_field:0x4->metadata,bundle(symmetric_l4,0,hrw,ofport,slaves:[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40]])'])
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -208,3 +218,59 @@ NXST_FLOW reply:
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([bundle action with ports up and down])
+AT_KEYWORDS([bundle_action])
+OVS_VSWITCHD_START([dnl
+    add-port br0 p1 -- set Interface p1 type=dummy -- \
+    set Interface p1 ofport_request=1 -- \
+    add-port br0 p2 -- set Interface p2 type=dummy -- \
+    set Interface p2 ofport_request=2
+])
+AT_CHECK([ovs-ofctl add-flow br0 'actions=bundle(eth_src,50,hrw,ofport,slaves:1,2)'])
+AT_CHECK([ovs-ofctl mod-port br0 p1 up])
+AT_CHECK([ovs-ofctl mod-port br0 p2 up])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 1
+])
+AT_CHECK([ovs-ofctl mod-port br0 p1 down])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 2
+])
+AT_CHECK([ovs-ofctl mod-port br0 p2 down])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: drop
+])
+AT_CHECK([ovs-ofctl mod-port br0 p1 up])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 1
+])
+AT_CHECK([ovs-ofctl mod-port br0 p2 up])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([bundle_load action with ports down])
+AT_KEYWORDS([bundle_action])
+OVS_VSWITCHD_START([dnl
+    add-port br0 p1 -- set Interface p1 type=dummy -- \
+    set Interface p1 ofport_request=1 -- \
+    add-port br0 p2 -- set Interface p2 type=dummy -- \
+    set Interface p2 ofport_request=2
+])
+AT_CHECK([ovs-ofctl add-flow br0 'actions=bundle_load(eth_src,50,hrw,ofport,OXM_OF_ETH_SRC[[0..15]],slaves:1,2)'])
+AT_CHECK([ovs-ofctl mod-port br0 p1 down])
+AT_CHECK([ovs-ofctl mod-port br0 p2 down])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout])
+AT_CHECK([grep Final stdout], [0],
+  [Final flow: in_port=LOCAL,vlan_tci=0x0000,dl_src=50:54:00:00:ff:ff,dl_dst=50:54:00:00:00:06,dl_type=0x0000
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
