Correct floating IP extra attributes updating issues

Updating floating IP extra attributes, for instance description,
will unexpectedly disassociate it. This behavior will interrupt the
user's service traffic. And this is because that user can submit an
empty request dict (without port_id parameter) for the floating IP
updating API, and then it will be disassociated by default.
So there is no way to update the floating IP extra attributes without
changing it's association.

This patch will make updating floating IP extra attributes API works
properly.

Closes-Bug: #1607746
Change-Id: I036e473118431856550249359a22445380ef9ece
diff --git a/neutron/tests/tempest/api/test_floating_ips.py b/neutron/tests/tempest/api/test_floating_ips.py
index 8ccdd44..bafa54c 100644
--- a/neutron/tests/tempest/api/test_floating_ips.py
+++ b/neutron/tests/tempest/api/test_floating_ips.py
@@ -71,3 +71,33 @@
         self.assertEqual('d2', body['floatingip']['description'])
         body = self.client.show_floatingip(body['floatingip']['id'])
         self.assertEqual('d2', body['floatingip']['description'])
+        # disassociate
+        body = self.client.update_floatingip(body['floatingip']['id'],
+                                             port_id=None)
+        self.assertEqual('d2', body['floatingip']['description'])
+
+    @test.idempotent_id('fd7161e1-2167-4686-a6ff-0f3df08001bb')
+    @test.requires_ext(extension="standard-attr-description",
+                       service="network")
+    def test_floatingip_update_extra_attributes_port_id_not_changed(self):
+        port_id = self.ports[1]['id']
+        body = self.client.create_floatingip(
+            floating_network_id=self.ext_net_id,
+            port_id=port_id,
+            description='d1'
+        )['floatingip']
+        self.assertEqual('d1', body['description'])
+        body = self.client.show_floatingip(body['id'])['floatingip']
+        self.assertEqual(port_id, body['port_id'])
+        # Update description
+        body = self.client.update_floatingip(body['id'], description='d2')
+        self.assertEqual('d2', body['floatingip']['description'])
+        # Floating IP association is not changed.
+        self.assertEqual(port_id, body['floatingip']['port_id'])
+        body = self.client.show_floatingip(body['floatingip']['id'])
+        self.assertEqual('d2', body['floatingip']['description'])
+        self.assertEqual(port_id, body['floatingip']['port_id'])
+        # disassociate
+        body = self.client.update_floatingip(body['floatingip']['id'],
+                                             port_id=None)
+        self.assertEqual(None, body['floatingip']['port_id'])
diff --git a/neutron/tests/tempest/api/test_revisions.py b/neutron/tests/tempest/api/test_revisions.py
index b45990f..ec82635 100644
--- a/neutron/tests/tempest/api/test_revisions.py
+++ b/neutron/tests/tempest/api/test_revisions.py
@@ -153,3 +153,5 @@
         b2 = self.client.update_floatingip(body['id'], description='d2')
         self.assertGreater(b2['floatingip']['revision_number'],
                            body['revision_number'])
+        # disassociate
+        self.client.update_floatingip(b2['floatingip']['id'], port_id=None)