]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Fix backups.rpcapi to pass objects over RPC
authorMichal Dulko <michal.dulko@intel.com>
Tue, 23 Jun 2015 13:42:00 +0000 (15:42 +0200)
committerMichal Dulko <michal.dulko@intel.com>
Tue, 23 Jun 2015 13:42:00 +0000 (15:42 +0200)
Commit Icff37261b367463b71a1268be16f9c97f595bf0c brought bugs in
backup.rpcapi. For export_record, import_record and reset_status
backup.id is passed instead of whole backup object - which
backup.manager expects. This causes severe failures. This commit
changes the rpcapi to pass objects and adds missing
backup.test_rpcapi unit tests that will prevent such failures in
the future.

Change-Id: I303b77214785165d37663fe8d502988e86a7950f
Closes-Bug: 1467841

cinder/backup/rpcapi.py
cinder/tests/unit/backup/test_rpcapi.py [new file with mode: 0644]
cinder/tests/unit/fake_backup.py [new file with mode: 0644]

index 5b5f17e644f3e362878dff6a83b6f9302a74c464..75575bc62130f92fcc8de491dafa8df9b7e2c919 100644 (file)
@@ -71,7 +71,7 @@ class BackupAPI(object):
                   {'id': backup.id,
                    'host': backup.host})
         cctxt = self.client.prepare(server=backup.host)
-        return cctxt.call(ctxt, 'export_record', backup_id=backup.id)
+        return cctxt.call(ctxt, 'export_record', backup=backup)
 
     def import_record(self,
                       ctxt,
@@ -87,7 +87,7 @@ class BackupAPI(object):
                    'url': backup_url})
         cctxt = self.client.prepare(server=host)
         cctxt.cast(ctxt, 'import_record',
-                   backup_id=backup.id,
+                   backup=backup,
                    backup_service=backup_service,
                    backup_url=backup_url,
                    backup_hosts=backup_hosts)
@@ -98,5 +98,4 @@ class BackupAPI(object):
                   {'id': backup.id,
                    'host': backup.host})
         cctxt = self.client.prepare(server=backup.host)
-        return cctxt.cast(ctxt, 'reset_status', backup_id=backup.id,
-                          status=status)
+        return cctxt.cast(ctxt, 'reset_status', backup=backup, status=status)
diff --git a/cinder/tests/unit/backup/test_rpcapi.py b/cinder/tests/unit/backup/test_rpcapi.py
new file mode 100644 (file)
index 0000000..049b710
--- /dev/null
@@ -0,0 +1,127 @@
+#    Copyright (c) 2015 Intel Corporation
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+"""
+Unit Tests for cinder.backup.rpcapi
+"""
+
+import copy
+
+import mock
+from oslo_config import cfg
+
+from cinder.backup import rpcapi as backup_rpcapi
+from cinder import context
+from cinder import objects
+from cinder import test
+from cinder.tests.unit import fake_backup
+
+
+CONF = cfg.CONF
+
+
+class BackupRpcAPITestCase(test.TestCase):
+    def setUp(self):
+        super(BackupRpcAPITestCase, self).setUp()
+        self.context = context.RequestContext('fake_user', 'fake_project')
+        self.fake_backup_obj = fake_backup.fake_backup_obj(self.context)
+
+    def _test_backup_api(self, method, rpc_method, server=None, fanout=False,
+                         **kwargs):
+        rpcapi = backup_rpcapi.BackupAPI()
+        expected_retval = 'foo' if rpc_method == 'call' else None
+
+        target = {
+            "server": server,
+            "fanout": fanout,
+            "version": kwargs.pop('version', rpcapi.BASE_RPC_API_VERSION)
+        }
+
+        expected_msg = copy.deepcopy(kwargs)
+
+        self.fake_args = None
+        self.fake_kwargs = None
+
+        def _fake_prepare_method(*args, **kwds):
+            for kwd in kwds:
+                self.assertEqual(target[kwd], kwds[kwd])
+            return rpcapi.client
+
+        def _fake_rpc_method(*args, **kwargs):
+            self.fake_args = args
+            self.fake_kwargs = kwargs
+            if expected_retval:
+                return expected_retval
+
+        with mock.patch.object(rpcapi.client, "prepare") as mock_prepared:
+            mock_prepared.side_effect = _fake_prepare_method
+
+            with mock.patch.object(rpcapi.client, rpc_method) as mock_method:
+                mock_method.side_effect = _fake_rpc_method
+                retval = getattr(rpcapi, method)(self.context, **kwargs)
+                self.assertEqual(expected_retval, retval)
+                expected_args = [self.context, method, expected_msg]
+                for arg, expected_arg in zip(self.fake_args, expected_args):
+                    self.assertEqual(expected_arg, arg)
+
+                for kwarg, value in self.fake_kwargs.items():
+                    if isinstance(value, objects.Backup):
+                        expected_back = expected_msg[kwarg].obj_to_primitive()
+                        backup = value.obj_to_primitive()
+                        self.assertEqual(expected_back, backup)
+                    else:
+                        self.assertEqual(expected_msg[kwarg], value)
+
+    def test_create_backup(self):
+        self._test_backup_api('create_backup',
+                              rpc_method='cast',
+                              server=self.fake_backup_obj.host,
+                              backup=self.fake_backup_obj)
+
+    def test_restore_backup(self):
+        self._test_backup_api('restore_backup',
+                              rpc_method='cast',
+                              server='fake_volume_host',
+                              volume_host='fake_volume_host',
+                              backup=self.fake_backup_obj,
+                              volume_id='fake_volume_id')
+
+    def test_delete_backup(self):
+        self._test_backup_api('delete_backup',
+                              rpc_method='cast',
+                              server=self.fake_backup_obj.host,
+                              backup=self.fake_backup_obj)
+
+    def test_export_record(self):
+        self._test_backup_api('export_record',
+                              rpc_method='call',
+                              server=self.fake_backup_obj.host,
+                              backup=self.fake_backup_obj)
+
+    def test_import_record(self):
+        self._test_backup_api('import_record',
+                              rpc_method='cast',
+                              server='fake_volume_host',
+                              host='fake_volume_host',
+                              backup=self.fake_backup_obj,
+                              backup_service='fake_service',
+                              backup_url='fake_url',
+                              backup_hosts=['fake_host1', 'fake_host2'])
+
+    def test_reset_status(self):
+        self._test_backup_api('reset_status',
+                              rpc_method='cast',
+                              server=self.fake_backup_obj.host,
+                              backup=self.fake_backup_obj,
+                              status='error')
diff --git a/cinder/tests/unit/fake_backup.py b/cinder/tests/unit/fake_backup.py
new file mode 100644 (file)
index 0000000..a3323d2
--- /dev/null
@@ -0,0 +1,54 @@
+#    Copyright (c) 2015 Intel Corporation
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_versionedobjects import fields
+
+from cinder import objects
+
+
+def fake_db_backup(**updates):
+    db_backup = {
+        'id': 1,
+        'user_id': 'fake_user',
+        'project_id': 'fake_project',
+        'volume_id': 'fake_id',
+        'status': 'creating',
+        'host': 'fake_host',
+        'display_name': 'fake_name',
+        'size': 5,
+        'display_description': 'fake_description',
+        'service_metadata': 'fake_metadata',
+        'service': 'fake_service',
+        'object_count': 5
+    }
+
+    for name, field in objects.Backup.fields.items():
+        if name in db_backup:
+            continue
+        if field.nullable:
+            db_backup[name] = None
+        elif field.default != fields.UnspecifiedDefault:
+            db_backup[name] = field.default
+        else:
+            raise Exception('fake_db_backup needs help with %s' % name)
+
+    if updates:
+        db_backup.update(updates)
+
+    return db_backup
+
+
+def fake_backup_obj(context, **updates):
+    return objects.Backup._from_db_object(context, objects.Backup(),
+                                          fake_db_backup(**updates))