From f23322c5ef59704462330e882dc97694c1d9a7c1 Mon Sep 17 00:00:00 2001
From: "Dr. Jens Harbott" <harbott@osism.tech>
Date: Tue, 6 Dec 2022 23:44:46 +0100
Subject: [PATCH] Fix parameter handling in server add fixed ip cmd

The fixed_ip_address parameter needs to be passed in a hash with key
"ip_address" in order to be processed by the server, the previous arg
was simply being ignored.

Added a functional test for better coverage.

Closes-Bug: 1998927

Change-Id: I6956d2642d8e80fc10c3739f0a571aa7ba276b1a
---
 openstackclient/compute/v2/server.py          | 12 ++--
 .../functional/compute/v2/test_server.py      | 56 +++++++++++++++++++
 .../tests/unit/compute/v2/test_server.py      | 13 ++---
 3 files changed, 67 insertions(+), 14 deletions(-)

diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 7afacb3eb0..098df4d19f 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -290,9 +290,11 @@ class AddFixedIP(command.ShowOne):
             return ((), ())
 
         kwargs = {
-            'net_id': net_id,
-            'fixed_ip': parsed_args.fixed_ip_address,
+            'net_id': net_id
         }
+        if parsed_args.fixed_ip_address:
+            kwargs['fixed_ips'] = [
+                {"ip_address": parsed_args.fixed_ip_address}]
         if parsed_args.tag:
             kwargs['tag'] = parsed_args.tag
 
@@ -451,8 +453,7 @@ class AddPort(command.Command):
             port_id = parsed_args.port
 
         kwargs = {
-            'port_id': port_id,
-            'fixed_ip': None,
+            'port_id': port_id
         }
 
         if parsed_args.tag:
@@ -506,8 +507,7 @@ class AddNetwork(command.Command):
             net_id = parsed_args.network
 
         kwargs = {
-            'net_id': net_id,
-            'fixed_ip': None,
+            'net_id': net_id
         }
 
         if parsed_args.tag:
diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py
index 830b3543b6..6a6afa7c48 100644
--- a/openstackclient/tests/functional/compute/v2/test_server.py
+++ b/openstackclient/tests/functional/compute/v2/test_server.py
@@ -1250,6 +1250,62 @@ class ServerTests(common.ComputeTestCase):
         addresses = cmd_output['addresses']['private']
         self.assertNotIn(ip_address, addresses)
 
+    def test_server_add_fixed_ip(self):
+        name = uuid.uuid4().hex
+        cmd_output = self.openstack(
+            'server create ' +
+            '--network private ' +
+            '--flavor ' + self.flavor_name + ' ' +
+            '--image ' + self.image_name + ' ' +
+            '--wait ' +
+            name,
+            parse_output=True,
+        )
+
+        self.assertIsNotNone(cmd_output['id'])
+        self.assertEqual(name, cmd_output['name'])
+        self.addCleanup(self.openstack, 'server delete --wait ' + name)
+
+        # create port, record its ip address to use in later call,
+        # then delete - this is to figure out what should be a free ip
+        # in the subnet
+        port_name = uuid.uuid4().hex
+
+        cmd_output = self.openstack(
+            'port list',
+            parse_output=True,
+        )
+        self.assertNotIn(port_name, cmd_output)
+
+        cmd_output = self.openstack(
+            'port create ' +
+            '--network private ' + port_name,
+            parse_output=True,
+        )
+        self.assertIsNotNone(cmd_output['id'])
+        ip_address = cmd_output['fixed_ips'][0]['ip_address']
+        self.openstack('port delete ' + port_name)
+
+        # add fixed ip to server, assert the ip address appears
+        self.openstack('server add fixed ip --fixed-ip-address ' + ip_address +
+                       ' ' + name + ' private')
+
+        wait_time = 0
+        while wait_time < 60:
+            cmd_output = self.openstack(
+                'server show ' + name,
+                parse_output=True,
+            )
+            if ip_address not in cmd_output['addresses']['private']:
+                # Hang out for a bit and try again
+                print('retrying add fixed ip check')
+                wait_time += 10
+                time.sleep(10)
+            else:
+                break
+        addresses = cmd_output['addresses']['private']
+        self.assertIn(ip_address, addresses)
+
     def test_server_add_remove_volume(self):
         volume_wait_for = volume_common.BaseVolumeTests.wait_for_status
 
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index d7e84ba381..9ee9791398 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -423,8 +423,7 @@ class TestServerAddFixedIP(TestServer):
             self.assertEqual(expected_data, tuple(data))
             self.sdk_client.create_server_interface.assert_called_once_with(
                 servers[0].id,
-                net_id=network['id'],
-                fixed_ip=None
+                net_id=network['id']
             )
 
     @mock.patch.object(sdk_utils, 'supports_microversion')
@@ -479,7 +478,7 @@ class TestServerAddFixedIP(TestServer):
             self.sdk_client.create_server_interface.assert_called_once_with(
                 servers[0].id,
                 net_id=network['id'],
-                fixed_ip='5.6.7.8'
+                fixed_ips=[{'ip_address': '5.6.7.8'}]
             )
 
     @mock.patch.object(sdk_utils, 'supports_microversion')
@@ -536,7 +535,7 @@ class TestServerAddFixedIP(TestServer):
             self.sdk_client.create_server_interface.assert_called_once_with(
                 servers[0].id,
                 net_id=network['id'],
-                fixed_ip='5.6.7.8',
+                fixed_ips=[{'ip_address': '5.6.7.8'}],
                 tag='tag1',
             )
 
@@ -847,7 +846,7 @@ class TestServerAddPort(TestServer):
         result = self.cmd.take_action(parsed_args)
 
         self.sdk_client.create_server_interface.assert_called_once_with(
-            servers[0], port_id=port_id, fixed_ip=None)
+            servers[0], port_id=port_id)
         self.assertIsNone(result)
 
     def test_server_add_port(self):
@@ -885,7 +884,6 @@ class TestServerAddPort(TestServer):
         self.sdk_client.create_server_interface.assert_called_once_with(
             servers[0],
             port_id='fake-port',
-            fixed_ip=None,
             tag='tag1')
 
     @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False)
@@ -1288,7 +1286,7 @@ class TestServerAddNetwork(TestServer):
         result = self.cmd.take_action(parsed_args)
 
         self.sdk_client.create_server_interface.assert_called_once_with(
-            servers[0], net_id=net_id, fixed_ip=None)
+            servers[0], net_id=net_id)
         self.assertIsNone(result)
 
     def test_server_add_network(self):
@@ -1327,7 +1325,6 @@ class TestServerAddNetwork(TestServer):
         self.sdk_client.create_server_interface.assert_called_once_with(
             servers[0],
             net_id='fake-network',
-            fixed_ip=None,
             tag='tag1'
         )