Skip to content

Commit

Permalink
[VNC API server] Resource list with list/map property fields
Browse files Browse the repository at this point in the history
Fix resource list when fields contains list or map properties

Change-Id: I320b1b99797fd2c2c56ee63c73bf0172473bd60b
Closes-Bug: #1592371
  • Loading branch information
Édouard Thuleau committed Jun 14, 2016
1 parent 278a48a commit cd1af2a
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 19 deletions.
53 changes: 53 additions & 0 deletions src/config/api-server/tests/test_crud_basic.py
Expand Up @@ -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


Expand Down Expand Up @@ -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


Expand Down
57 changes: 38 additions & 19 deletions src/config/common/vnc_cassandra.py
Expand Up @@ -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:
Expand Down

0 comments on commit cd1af2a

Please sign in to comment.