diff --git a/trove_dashboard/content/database_clusters/forms.py b/trove_dashboard/content/database_clusters/forms.py index 5cbb87d..c7149b4 100644 --- a/trove_dashboard/content/database_clusters/forms.py +++ b/trove_dashboard/content/database_clusters/forms.py @@ -238,7 +238,18 @@ class LaunchForm(forms.SelfHandlingForm): # only add to choices if datastore has at least one version version_choices = () for v in versions: - if hasattr(v, 'active') and not v.active: + # NOTE(zhaochao): troveclient API resources are lazy + # loading objects. When an attribute is not found, the + # get() method of the Manager object will be called + # with the ID of the resource. However for + # datastore_versions, the get() method is expecting two + # arguments: datastore and datastore_version(name), so + # TypeError will be raised as not enough arguments are + # passed. In Python 2.x, hasattr() won't reraise the + # exception(which is not correct), but reraise under + # Python 3(which should be correct). + # Use v.to_dict() to verify the 'active' info instead. + if not v.to_dict().get('active', True): continue selection_text = self._build_datastore_display_text( ds.name, v.name) diff --git a/trove_dashboard/content/database_configurations/tests.py b/trove_dashboard/content/database_configurations/tests.py index 20fba8f..ea0c291 100644 --- a/trove_dashboard/content/database_configurations/tests.py +++ b/trove_dashboard/content/database_configurations/tests.py @@ -12,7 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -import copy import logging import six @@ -378,7 +377,26 @@ class DatabaseConfigurationsTests(test.TestCase): 'configuration_update',), config_param_manager: ('get',)}) def test_values_tab_apply_action(self): - config = copy.deepcopy(self.database_configurations.first()) + # NOTE(zhaochao): we cannot use copy.deepcopy() under Python 3, + # because of the lazy-loading feature of the troveclient Resource + # objects. copy.deepcopy will use hasattr to search for the + # '__setstate__' attribute of the resource object. As the resource + # object is lazy loading, searching attributes relys on the 'is_load' + # property, unfortunately this property is also not loaded at the + # moment, then we're getting in an infinite loop there. Python will + # raise RuntimeError saying "maximum recursion depth exceeded", this is + # ignored under Python 2.x, but reraised under Python 3 by hasattr(). + # + # Temporarily importing troveclient and reconstructing a configuration + # object from the original config object's dict info will make this + # case (and the next) working under Python 3. + original_config = self.database_configurations.first() + from troveclient.v1 import configurations + config = configurations.Configuration( + configurations.Configurations(None), original_config.to_dict()) + # Making sure the newly constructed config object is the same as + # the original one. + self.assertEqual(config, original_config) # setup the configuration parameter manager config_param_mgr = config_param_manager.ConfigParamManager( @@ -412,7 +430,16 @@ class DatabaseConfigurationsTests(test.TestCase): 'configuration_update',), config_param_manager: ('get',)}) def test_values_tab_apply_action_exception(self): - config = copy.deepcopy(self.database_configurations.first()) + # NOTE(zhaochao) Please refer to the comment at the beginning of the + # 'test_values_tab_apply_action' about not using copy.deepcopy() for + # details. + original_config = self.database_configurations.first() + from troveclient.v1 import configurations + config = configurations.Configuration( + configurations.Configurations(None), original_config.to_dict()) + # Making sure the newly constructed config object is the same as + # the original one. + self.assertEqual(config, original_config) # setup the configuration parameter manager config_param_mgr = config_param_manager.ConfigParamManager( diff --git a/trove_dashboard/content/databases/workflows/create_instance.py b/trove_dashboard/content/databases/workflows/create_instance.py index 281dfde..4f120ba 100644 --- a/trove_dashboard/content/databases/workflows/create_instance.py +++ b/trove_dashboard/content/databases/workflows/create_instance.py @@ -211,7 +211,12 @@ class SetInstanceDetailsAction(workflows.Action): # only add to choices if datastore has at least one version version_choices = () for v in versions: - if hasattr(v, 'active') and not v.active: + # NOTE(zhaochao): please refer to the comment about + # the same change for 'populate_datastore_choices' + # of 'LaunchForm' in + # trove_dashboard/content/database_clusters/forms.py + # for details. + if not v.to_dict().get('active', True): continue if self.backup_id: if v.id != backup.datastore['version_id']: