From 724370b0a41ec02e03ab0a517aa8ef391ba8a4f6 Mon Sep 17 00:00:00 2001
From: Sahid Orentino Ferdjaoui <sahid.ferdjaoui@redhat.com>
Date: Tue, 25 Apr 2017 06:06:07 -0400
Subject: [PATCH 09/10] libvirt: update logic to configure device for scsi
 controller

When using virtio-scsi it is possible to handle up to 256 disk but
because we do not specifically set the information about the
controller only 6 can be handled.

This commit is fixing the issue by adding the address element to the
disk configuration.

 Conflicts:
	nova/virt/libvirt/driver.py

Closes-Bug: #1686116
Change-Id: I98e53b378cc99747765066001a0b51880543d2dd
(cherry picked from commit fb343c402296c109af49bfca2f909680ca38a72b)
(cherry picked from commit 70ede864e74460d3cfde1c99c6a2879146b52dc4)
---
 nova/tests/unit/virt/libvirt/fake_imagebackend.py |  3 ++-
 nova/tests/unit/virt/libvirt/test_driver.py       | 18 +++++++++++++++--
 nova/virt/libvirt/driver.py                       | 24 +++++++++++++++++++++--
 nova/virt/libvirt/imagebackend.py                 | 23 ++++++++++++++++++++--
 4 files changed, 61 insertions(+), 7 deletions(-)

diff --git a/nova/tests/unit/virt/libvirt/fake_imagebackend.py b/nova/tests/unit/virt/libvirt/fake_imagebackend.py
index d92e5a370b..2b3b9db9f3 100644
--- a/nova/tests/unit/virt/libvirt/fake_imagebackend.py
+++ b/nova/tests/unit/virt/libvirt/fake_imagebackend.py
@@ -167,7 +167,8 @@ class ImageBackendFixture(fixtures.Fixture):
             self.imported_files.append((local_filename, remote_filename))
 
     def _fake_libvirt_info(self, mock_disk, disk_bus, disk_dev, device_type,
-                     cache_mode, extra_specs, hypervisor_version):
+                           cache_mode, extra_specs, hypervisor_version,
+                           disk_unit=None):
         # For tests in test_virt_drivers which expect libvirt_info to be
         # functional
         info = config.LibvirtConfigGuestDisk()
diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py
index 467c862d41..7fe8779e8f 100644
--- a/nova/tests/unit/virt/libvirt/test_driver.py
+++ b/nova/tests/unit/virt/libvirt/test_driver.py
@@ -3375,12 +3375,24 @@ class LibvirtConnTestCase(test.NoDBTestCase):
                               vconfig.LibvirtConfigGuestDisk)
         self.assertEqual(cfg.devices[2].target_dev, disk)
 
-    def test_get_guest_config_with_virtio_scsi_bus(self):
+    def test_get_guest_config_default_with_virtio_scsi_bus(self):
+        self._test_get_guest_config_with_virtio_scsi_bus()
+
+    @mock.patch.object(rbd_utils.RBDDriver, 'get_mon_addrs')
+    @mock.patch.object(rbd_utils, 'rbd')
+    def test_get_guest_config_rbd_with_virtio_scsi_bus(
+            self, mock_rdb, mock_get_mon_addrs):
+        self.flags(images_type='rbd', group='libvirt')
+        mock_get_mon_addrs.return_value = ("host", 9876)
+        self._test_get_guest_config_with_virtio_scsi_bus()
+
+    def _test_get_guest_config_with_virtio_scsi_bus(self):
         drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
 
         image_meta = objects.ImageMeta.from_dict({
             "disk_format": "raw",
-            "properties": {"hw_scsi_model": "virtio-scsi"}})
+            "properties": {"hw_scsi_model": "virtio-scsi",
+                           "hw_disk_bus": "scsi"}})
         instance_ref = objects.Instance(**self.test_instance)
 
         disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
@@ -3390,8 +3402,10 @@ class LibvirtConnTestCase(test.NoDBTestCase):
         cfg = drvr._get_guest_config(instance_ref, [], image_meta, disk_info)
         self.assertIsInstance(cfg.devices[0],
                          vconfig.LibvirtConfigGuestDisk)
+        self.assertEqual(0, cfg.devices[0].device_addr.unit)
         self.assertIsInstance(cfg.devices[1],
                          vconfig.LibvirtConfigGuestDisk)
+        self.assertEqual(1, cfg.devices[1].device_addr.unit)
         self.assertIsInstance(cfg.devices[2],
                          vconfig.LibvirtConfigGuestController)
         self.assertEqual(cfg.devices[2].model, 'virtio-scsi')
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index 7d4474ddeb..3268dd29a3 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -3467,6 +3467,7 @@ class LibvirtDriver(driver.ComputeDriver):
                       {'qemu': MIN_QEMU_DISCARD_VERSION})
                 raise exception.Invalid(msg)
 
+        disk_unit = None
         image = self.image_backend.image(instance,
                                          name,
                                          image_type)
@@ -3484,12 +3485,17 @@ class LibvirtDriver(driver.ComputeDriver):
                 LOG.debug('Config drive not found in RBD, falling back to the '
                           'instance directory', instance=instance)
         disk_info = disk_mapping[name]
-        return image.libvirt_info(disk_info['bus'],
+        if 'unit' in disk_mapping:
+            disk_unit = disk_mapping['unit']
+            disk_mapping['unit'] += 1  # Increments for the next disk added
+        conf = image.libvirt_info(disk_info['bus'],
                                   disk_info['dev'],
                                   disk_info['type'],
                                   self.disk_cachemode,
                                   inst_type['extra_specs'],
-                                  self._host.get_version())
+                                  self._host.get_version(),
+                                  disk_unit=disk_unit)
+        return conf
 
     def _get_guest_fs_config(self, instance, name, image_type=None):
         image = self.image_backend.image(instance,
@@ -3509,6 +3515,19 @@ class LibvirtDriver(driver.ComputeDriver):
         mount_rootfs = CONF.libvirt.virt_type == "lxc"
         scsi_controller = self._get_scsi_controller(image_meta)
 
+        if scsi_controller and scsi_controller.model == 'virtio-scsi':
+            # The virtio-scsi can handle up to 256 devices but the
+            # optional element "address" must be defined to describe
+            # where the device is placed on the controller (see:
+            # LibvirtConfigGuestDeviceAddressDrive).
+            #
+            # Note about why it's added in disk_mapping: It's not
+            # possible to pass an 'int' by reference in Python, so we
+            # use disk_mapping as container to keep reference of the
+            # unit added and be able to increment it for each disk
+            # added.
+            disk_mapping['unit'] = 0
+
         if mount_rootfs:
             fs = vconfig.LibvirtConfigGuestFilesys()
             fs.source_type = "mount"
@@ -3611,6 +3630,7 @@ class LibvirtDriver(driver.ComputeDriver):
             scsi_controller = vconfig.LibvirtConfigGuestController()
             scsi_controller.type = 'scsi'
             scsi_controller.model = hw_scsi_model
+            scsi_controller.index = 0
             return scsi_controller
 
     def _get_host_sysinfo_serial_hardware(self):
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py
index c24721aff3..59bf490219 100644
--- a/nova/virt/libvirt/imagebackend.py
+++ b/nova/virt/libvirt/imagebackend.py
@@ -118,7 +118,8 @@ class Image(object):
         pass
 
     def libvirt_info(self, disk_bus, disk_dev, device_type, cache_mode,
-                     extra_specs, hypervisor_version, boot_order=None):
+                     extra_specs, hypervisor_version, boot_order=None,
+                     disk_unit=None):
         """Get `LibvirtConfigGuestDisk` filled for this image.
 
         :disk_dev: Disk bus device name
@@ -144,10 +145,24 @@ class Image(object):
         info.source_path = self.path
         info.boot_order = boot_order
 
+        if disk_bus == 'scsi':
+            self.disk_scsi(info, disk_unit)
+
         self.disk_qos(info, extra_specs)
 
         return info
 
+    def disk_scsi(self, info, disk_unit):
+        # The driver is responsible to create the SCSI controller
+        # at index 0.
+        info.device_addr = vconfig.LibvirtConfigGuestDeviceAddressDrive()
+        info.device_addr.controller = 0
+        if disk_unit is not None:
+            # In order to allow up to 256 disks handled by one
+            # virtio-scsi controller, the device addr should be
+            # specified.
+            info.device_addr.unit = disk_unit
+
     def disk_qos(self, info, extra_specs):
         tune_items = ['disk_read_bytes_sec', 'disk_read_iops_sec',
             'disk_write_bytes_sec', 'disk_write_iops_sec',
@@ -788,7 +803,8 @@ class Rbd(Image):
         self.discard_mode = CONF.libvirt.hw_disk_discard
 
     def libvirt_info(self, disk_bus, disk_dev, device_type, cache_mode,
-            extra_specs, hypervisor_version, boot_order=None):
+                     extra_specs, hypervisor_version, boot_order=None,
+                     disk_unit=None):
         """Get `LibvirtConfigGuestDisk` filled for this image.
 
         :disk_dev: Disk bus device name
@@ -824,6 +840,9 @@ class Rbd(Image):
             info.auth_secret_type = 'ceph'
             info.auth_secret_uuid = CONF.libvirt.rbd_secret_uuid
 
+        if disk_bus == 'scsi':
+            self.disk_scsi(info, disk_unit)
+
         self.disk_qos(info, extra_specs)
 
         return info
-- 
2.13.7

