From cd1af2ab036c9bbc5523e37357a4979212bb4d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89douard=20Thuleau?= Date: Tue, 14 Jun 2016 16:27:09 +0200 Subject: [PATCH] [VNC API server] Resource list with list/map property fields Fix resource list when fields contains list or map properties Change-Id: I320b1b99797fd2c2c56ee63c73bf0172473bd60b Closes-Bug: #1592371 --- .../api-server/tests/test_crud_basic.py | 53 +++++++++++++++++ src/config/common/vnc_cassandra.py | 57 ++++++++++++------- 2 files changed, 91 insertions(+), 19 deletions(-) diff --git a/src/config/api-server/tests/test_crud_basic.py b/src/config/api-server/tests/test_crud_basic.py index 3508456275d..078f2021d44 100644 --- a/src/config/api-server/tests/test_crud_basic.py +++ b/src/config/api-server/tests/test_crud_basic.py @@ -2729,6 +2729,32 @@ def test_prop_list_wrong_type_should_fail(self): self.assertEqual(response.status_code, 400) # end test_prop_list_wrong_type_should_fail + def test_resource_list_with_field_prop_list(self): + vmi_obj = VirtualMachineInterface('vmi-%s' % (self.id()), + parent_obj=Project()) + fname = 'virtual_machine_interface_fat_flow_protocols' + # needed for backend type-specific handling + vmi_obj.add_virtual_network(VirtualNetwork()) + self._vnc_lib.virtual_machine_interface_create(vmi_obj) + + vmis = self._vnc_lib.virtual_machine_interfaces_list( + obj_uuids=[vmi_obj.uuid], fields=[fname]) + vmi_ids = [vmi['uuid'] for vmi in vmis['virtual-machine-interfaces']] + self.assertEqual([vmi_obj.uuid], vmi_ids) + self.assertNotIn(fname, vmis['virtual-machine-interfaces'][0]) + + vmi_obj = self._vnc_lib.virtual_machine_interface_read(id=vmi_obj.uuid) + proto_type = ProtocolType(protocol='proto', port='port') + vmi_obj.add_virtual_machine_interface_fat_flow_protocols(proto_type, + 'pos') + self._vnc_lib.virtual_machine_interface_update(vmi_obj) + vmis = self._vnc_lib.virtual_machine_interfaces_list( + obj_uuids=[vmi_obj.uuid], fields=[fname]) + vmi_ids = [vmi['uuid'] for vmi in vmis['virtual-machine-interfaces']] + self.assertEqual([vmi_obj.uuid], vmi_ids) + self.assertIn(fname, vmis['virtual-machine-interfaces'][0]) + self.assertDictEqual({'fat_flow_protocol': [vars(proto_type)]}, + vmis['virtual-machine-interfaces'][0][fname]) # end class TestPropertyWithlist @@ -2823,6 +2849,33 @@ def test_element_add_del_in_object(self): if binding.key not in self._excluded_vmi_bindings} self.assertDictEqual(bindings_dict, fake_bindings_dict) # end test_element_set_del_in_object + + def test_resource_list_with_field_prop_map(self): + vmi_obj = VirtualMachineInterface('vmi-%s' % (self.id()), + parent_obj=Project()) + fname = 'virtual_machine_interface_bindings' + # needed for backend type-specific handling + vmi_obj.add_virtual_network(VirtualNetwork()) + self._vnc_lib.virtual_machine_interface_create(vmi_obj) + + vmis = self._vnc_lib.virtual_machine_interfaces_list( + obj_uuids=[vmi_obj.uuid], fields=[fname]) + vmi_ids = [vmi['uuid'] for vmi in vmis['virtual-machine-interfaces']] + self.assertEqual([vmi_obj.uuid], vmi_ids) + self.assertNotIn(fname, vmis['virtual-machine-interfaces'][0]) + + vmi_obj = self._vnc_lib.virtual_machine_interface_read(id=vmi_obj.uuid) + kv_pairs = KeyValuePairs([KeyValuePair(key='k', value='v')]) + vmi_obj.set_virtual_machine_interface_bindings(kv_pairs) + self._vnc_lib.virtual_machine_interface_update(vmi_obj) + + vmis = self._vnc_lib.virtual_machine_interfaces_list( + obj_uuids=[vmi_obj.uuid], fields=[fname]) + vmi_ids = [vmi['uuid'] for vmi in vmis['virtual-machine-interfaces']] + self.assertEqual([vmi_obj.uuid], vmi_ids) + self.assertIn(fname, vmis['virtual-machine-interfaces'][0]) + self.assertDictEqual(kv_pairs.exportDict()['KeyValuePairs'], + vmis['virtual-machine-interfaces'][0][fname]) # end class TestPropertyWithMap diff --git a/src/config/common/vnc_cassandra.py b/src/config/common/vnc_cassandra.py index 5f1169107b4..bf0420850a2 100644 --- a/src/config/common/vnc_cassandra.py +++ b/src/config/common/vnc_cassandra.py @@ -590,35 +590,54 @@ def object_read(self, res_type, obj_uuids, field_names=None): # if field_names=None, all fields will be read/returned obj_type = res_type.replace('-', '_') obj_class = self._get_resource_class(obj_type) + ref_fields = obj_class.ref_fields + backref_fields = obj_class.backref_fields + children_fields = obj_class.children_fields + list_fields = obj_class.prop_list_fields + map_fields = obj_class.prop_map_fields + prop_fields = obj_class.prop_fields - (list_fields | map_fields) # optimize for common case of reading non-backref, non-children fields # ignoring columns starting from 'b' and 'c' - significant performance # impact in scaled setting. e.g. read of project - columns = set([]) - column_start = '' - column_finish = '' + obj_rows = {} if (field_names is None or - (set(field_names) & (obj_class.backref_fields | - obj_class.children_fields))): + set(field_names) & (backref_fields | children_fields)): # atleast one backref/children field is needed - column_start = '' - elif not set(field_names) & (obj_class.ref_fields): + obj_rows = self.multiget(self._OBJ_UUID_CF_NAME, + obj_uuids, + timestamp=True) + elif not set(field_names) & ref_fields: # specific props have been asked fetch exactly those - column_start = 'parent:' - column_finish = 'parent;' columns = set(['type', 'fq_name', 'parent_type']) - for fname in field_names: - if fname in obj_class.prop_fields: - columns.add('prop:' + fname) + for fname in set(field_names) & prop_fields: + columns.add('prop:' + fname) + obj_rows = self.multiget(self._OBJ_UUID_CF_NAME, + obj_uuids, + columns=list(columns), + start='parent:', + finish='parent;', + timestamp=True) + for fname in set(field_names) & list_fields: + merge_dict(obj_rows, + self.multiget(self._OBJ_UUID_CF_NAME, + obj_uuids, + start='propl:%s:' % fname, + finish='propl:%s;' % fname, + timestamp=True)) + for fname in set(field_names) & map_fields: + merge_dict(obj_rows, + self.multiget(self._OBJ_UUID_CF_NAME, + obj_uuids, + start='propm:%s:' % fname, + finish='propm:%s;' % fname, + timestamp=True)) else: # ignore reading backref + children columns - column_start = 'd' - obj_rows = self.multiget(self._OBJ_UUID_CF_NAME, - obj_uuids, - columns=list(columns), - start=column_start, - finish=column_finish, - timestamp=True) + obj_rows = self.multiget(self._OBJ_UUID_CF_NAME, + obj_uuids, + start='d', + timestamp=True) if not obj_rows: if len(obj_uuids) == 1: