From a50dfbdd47b2f7bb31320f31b9c8c9c91c8c6f5e Mon Sep 17 00:00:00 2001
From: Matt Riedemann <mriedem.os@gmail.com>
Date: Wed, 24 Apr 2019 19:00:35 -0400
Subject: [PATCH] Always pass HostAPI to get_availability_zones

Since there were only three non-test locations that
called get_availability_zones, and they can all pass
a HostAPI instance, the hostapi kwarg is made a required
argument to the method. This also means the compute.API
class can create a HostAPI instance to use and provide
its own copy of the RPC ComputeAPI and service group API.

While in here the get_availability_zone docstring is
cleaned up a bit and the context param is documented.

Change-Id: I727a5001f9f75f4bd8c7a7b599d647cf987d1659
---
 .../openstack/compute/availability_zone.py    |  5 ++--
 nova/availability_zones.py                    | 30 ++++++++-----------
 nova/compute/api.py                           |  8 +++--
 nova/tests/fixtures.py                        |  3 +-
 nova/tests/unit/test_availability_zones.py    |  9 ++++--
 5 files changed, 27 insertions(+), 28 deletions(-)

diff --git a/nova/api/openstack/compute/availability_zone.py b/nova/api/openstack/compute/availability_zone.py
index 95ed886744cd..2a6f9f2759a9 100644
--- a/nova/api/openstack/compute/availability_zone.py
+++ b/nova/api/openstack/compute/availability_zone.py
@@ -46,7 +46,7 @@ class AvailabilityZoneController(wsgi.Controller):
         ctxt = context.elevated()
         available_zones, not_available_zones = \
             availability_zones.get_availability_zones(
-                ctxt, hostapi=self.host_api)
+                ctxt, self.host_api)
 
         filtered_available_zones = \
             self._get_filtered_availability_zones(available_zones, True)
@@ -64,8 +64,7 @@ class AvailabilityZoneController(wsgi.Controller):
 
         available_zones, not_available_zones = (
             availability_zones.get_availability_zones(
-                ctxt, enabled_services=enabled_services,
-                hostapi=self.host_api))
+                ctxt, self.host_api, enabled_services=enabled_services))
 
         zone_hosts = {}
         host_services = {}
diff --git a/nova/availability_zones.py b/nova/availability_zones.py
index 579efacb6cd7..d5651f75b883 100644
--- a/nova/availability_zones.py
+++ b/nova/availability_zones.py
@@ -109,27 +109,21 @@ def update_host_availability_zone_cache(context, host, availability_zone=None):
     cache.set(cache_key, availability_zone)
 
 
-def get_availability_zones(context, get_only_available=False,
-                           with_hosts=False, enabled_services=None,
-                           hostapi=None):
+def get_availability_zones(context, hostapi, get_only_available=False,
+                           with_hosts=False, enabled_services=None):
     """Return available and unavailable zones on demand.
 
-        :param get_only_available: flag to determine whether to return
-            available zones only, default False indicates return both
-            available zones and not available zones, True indicates return
-            available zones only
-        :param with_hosts: whether to return hosts part of the AZs
-        :type with_hosts: bool
-        :param enabled_services: list of enabled services to use; if None
-            enabled services will be retrieved from all cells with zones set
-        :param hostapi: nova.compute.api.HostAPI instance
+    :param context: nova auth RequestContext
+    :param hostapi: nova.compute.api.HostAPI instance
+    :param get_only_available: flag to determine whether to return
+        available zones only, default False indicates return both
+        available zones and not available zones, True indicates return
+        available zones only
+    :param with_hosts: whether to return hosts part of the AZs
+    :type with_hosts: bool
+    :param enabled_services: list of enabled services to use; if None
+        enabled services will be retrieved from all cells with zones set
     """
-    # TODO(mriedem): Make hostapi a required arg in a non-backportable FUP.
-    if hostapi is None:
-        # NOTE(danms): Avoid circular import
-        from nova import compute
-        hostapi = compute.HostAPI()
-
     if enabled_services is None:
         enabled_services = hostapi.service_get_all(
             context, {'disabled': False}, set_zones=True, all_cells=True)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 1e47cf2232f9..ebd117213e3a 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -272,6 +272,7 @@ class API(base.Base):
         self.compute_rpcapi = compute_rpcapi.ComputeAPI()
         self.compute_task_api = conductor.ComputeTaskAPI()
         self.servicegroup_api = servicegroup.API()
+        self.host_api = HostAPI(self.compute_rpcapi, self.servicegroup_api)
         self.notifier = rpc.get_notifier('compute', CONF.host)
         if CONF.ephemeral_storage_encryption.enabled:
             self.key_manager = key_manager.API()
@@ -1785,7 +1786,8 @@ class API(base.Base):
 
         if availability_zone:
             available_zones = availability_zones.\
-                get_availability_zones(context.elevated(), True)
+                get_availability_zones(context.elevated(), self.host_api,
+                                       get_only_available=True)
             if forced_host is None and availability_zone not in \
                     available_zones:
                 msg = _('The requested availability zone is not available')
@@ -4968,9 +4970,9 @@ def _find_service_in_cell(context, service_id=None, service_host=None):
 class HostAPI(base.Base):
     """Sub-set of the Compute Manager API for managing host operations."""
 
-    def __init__(self, rpcapi=None):
+    def __init__(self, rpcapi=None, servicegroup_api=None):
         self.rpcapi = rpcapi or compute_rpcapi.ComputeAPI()
-        self.servicegroup_api = servicegroup.API()
+        self.servicegroup_api = servicegroup_api or servicegroup.API()
         super(HostAPI, self).__init__()
 
     def _assert_host_exists(self, context, host_name, must_be_up=False):
diff --git a/nova/tests/fixtures.py b/nova/tests/fixtures.py
index 164829b47b4d..f23134740080 100644
--- a/nova/tests/fixtures.py
+++ b/nova/tests/fixtures.py
@@ -2220,7 +2220,8 @@ class AvailabilityZoneFixture(fixtures.Fixture):
         super(AvailabilityZoneFixture, self).setUp()
 
         def fake_get_availability_zones(
-                ctxt, get_only_available=False, with_hosts=False):
+                ctxt, hostapi, get_only_available=False,
+                with_hosts=False, enabled_services=None):
             # A 2-item tuple is returned if get_only_available=False.
             if not get_only_available:
                 return self.zones, []
diff --git a/nova/tests/unit/test_availability_zones.py b/nova/tests/unit/test_availability_zones.py
index e36061b277e9..020b7188a8cb 100644
--- a/nova/tests/unit/test_availability_zones.py
+++ b/nova/tests/unit/test_availability_zones.py
@@ -22,6 +22,7 @@ from oslo_utils.fixture import uuidsentinel
 import six
 
 from nova import availability_zones as az
+from nova.compute import api as compute_api
 import nova.conf
 from nova import context
 from nova.db import api as db
@@ -210,16 +211,18 @@ class AvailabilityZoneTestCases(test.TestCase):
         self._add_to_aggregate(service3, agg2)
         self._add_to_aggregate(service4, agg3)
 
-        zones, not_zones = az.get_availability_zones(self.context)
+        host_api = compute_api.HostAPI()
+        zones, not_zones = az.get_availability_zones(self.context, host_api)
 
         self.assertEqual(['nova-test', 'nova-test2'], zones)
         self.assertEqual(['nova-test3', 'nova'], not_zones)
 
-        zones = az.get_availability_zones(self.context, True)
+        zones = az.get_availability_zones(self.context, host_api,
+                                          get_only_available=True)
 
         self.assertEqual(['nova-test', 'nova-test2'], zones)
 
-        zones, not_zones = az.get_availability_zones(self.context,
+        zones, not_zones = az.get_availability_zones(self.context, host_api,
                                                      with_hosts=True)
 
         self.assertJsonEqual(zones,