@@ -869,6 +869,87 @@ def test_set_port_binding_and_admin_state(
869869 )
870870 assert res_toggle .is_admin_state_up is True
871871
872+ def test_add_remove_security_group_on_port (
873+ self , mock_openstack_connect_network
874+ ):
875+ mock_conn = mock_openstack_connect_network
876+
877+ # current without sg-9
878+ current = Mock ()
879+ current .id = "port-1"
880+ current .name = "p1"
881+ current .status = "ACTIVE"
882+ current .description = None
883+ current .project_id = None
884+ current .network_id = "net-1"
885+ current .admin_state_up = True
886+ current .is_admin_state_up = True
887+ current .device_id = None
888+ current .device_owner = None
889+ current .mac_address = "fa:16:3e:00:00:09"
890+ current .fixed_ips = []
891+ current .security_group_ids = ["sg-1" , "sg-2" ]
892+ mock_conn .network .get_port .return_value = current
893+
894+ # updated after add
895+ updated_add = Mock ()
896+ updated_add .id = "port-1"
897+ updated_add .name = "p1"
898+ updated_add .status = "ACTIVE"
899+ updated_add .description = None
900+ updated_add .project_id = None
901+ updated_add .network_id = "net-1"
902+ updated_add .admin_state_up = True
903+ updated_add .is_admin_state_up = True
904+ updated_add .device_id = None
905+ updated_add .device_owner = None
906+ updated_add .mac_address = "fa:16:3e:00:00:09"
907+ updated_add .fixed_ips = []
908+ updated_add .security_group_ids = ["sg-1" , "sg-2" , "sg-9" ]
909+ mock_conn .network .update_port .return_value = updated_add
910+
911+ tools = self .get_network_tools ()
912+ res_add = tools .add_security_group_to_port ("port-1" , "sg-9" )
913+ assert isinstance (res_add , Port )
914+ mock_conn .network .update_port .assert_called_with (
915+ "port-1" , security_groups = ["sg-1" , "sg-2" , "sg-9" ]
916+ )
917+
918+ # idempotent add when sg already present
919+ mock_conn .network .get_port .return_value = updated_add
920+ res_add_again = tools .add_security_group_to_port ("port-1" , "sg-9" )
921+ assert isinstance (res_add_again , Port )
922+
923+ # updated after remove
924+ updated_remove = Mock ()
925+ updated_remove .id = "port-1"
926+ updated_remove .name = "p1"
927+ updated_remove .status = "ACTIVE"
928+ updated_remove .description = None
929+ updated_remove .project_id = None
930+ updated_remove .network_id = "net-1"
931+ updated_remove .admin_state_up = True
932+ updated_remove .is_admin_state_up = True
933+ updated_remove .device_id = None
934+ updated_remove .device_owner = None
935+ updated_remove .mac_address = "fa:16:3e:00:00:09"
936+ updated_remove .fixed_ips = []
937+ updated_remove .security_group_ids = ["sg-1" , "sg-2" ]
938+ mock_conn .network .update_port .return_value = updated_remove
939+
940+ res_remove = tools .remove_security_group_from_port ("port-1" , "sg-9" )
941+ assert isinstance (res_remove , Port )
942+ mock_conn .network .update_port .assert_called_with (
943+ "port-1" , security_groups = ["sg-1" , "sg-2" ]
944+ )
945+
946+ # idempotent remove when sg not present
947+ mock_conn .network .get_port .return_value = updated_remove
948+ res_remove_again = tools .remove_security_group_from_port (
949+ "port-1" , "sg-9"
950+ )
951+ assert isinstance (res_remove_again , Port )
952+
872953 def test_get_subnets_filters_and_has_gateway_true (
873954 self ,
874955 mock_openstack_connect_network ,
0 commit comments