Merge "Add etcd db for VolumeMapping"

This commit is contained in:
Zuul 2017-12-10 08:53:49 +00:00 committed by Gerrit Code Review
commit 9d6fe8870d
4 changed files with 301 additions and 11 deletions

View File

@ -82,6 +82,8 @@ def translate_etcd_result(etcd_result, model_type):
ret = models.Capsule(data)
elif model_type == 'pcidevice':
ret = models.PciDevice(data)
elif model_type == 'volume_mapping':
ret = models.VolumeMapping(data)
else:
raise exception.InvalidParameterValue(
_('The model_type value: %s is invalid.'), model_type)
@ -861,3 +863,87 @@ class EtcdAPI(object):
raise
return translate_etcd_result(target, 'pcidevice')
def list_volume_mappings(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None):
try:
res = getattr(self.client.read(
'/volume_mappings'), 'children', None)
except etcd.EtcdKeyNotFound:
return []
except Exception as e:
LOG.error(
"Error occurred while reading from etcd server: %s",
six.text_type(e))
raise
volume_mappings = []
for vm in res:
if vm.value is not None:
volume_mappings.append(
translate_etcd_result(vm, 'volume_mapping'))
filters = self._add_tenant_filters(context, filters)
filtered_vms = self._filter_resources(volume_mappings, filters)
return self._process_list_result(filtered_vms, limit=limit,
sort_key=sort_key)
def create_volume_mapping(self, context, volume_mapping_data):
if not volume_mapping_data.get('uuid'):
volume_mapping_data['uuid'] = uuidutils.generate_uuid()
volume_mapping = models.VolumeMapping(volume_mapping_data)
try:
volume_mapping.save()
except Exception as e:
LOG.error('Error occurred while creating volume mapping: %s',
six.text_type(e))
raise
return volume_mapping
def get_volume_mapping_by_uuid(self, context, volume_mapping_uuid):
try:
res = self.client.read('/volume_mappings/' + volume_mapping_uuid)
volume_mapping = translate_etcd_result(res, 'volume_mapping')
filtered_vms = self._filter_resources(
[volume_mapping], self._add_tenant_filters(context, {}))
if filtered_vms:
return filtered_vms[0]
else:
raise exception.VolumeMappingNotFound(
volume_mapping=volume_mapping_uuid)
except etcd.EtcdKeyNotFound:
raise exception.VolumeMappingNotFound(
volume_mapping=volume_mapping_uuid)
except Exception as e:
LOG.error('Error occurred while retrieving volume mapping: %s',
six.text_type(e))
raise
def destroy_volume_mapping(self, context, volume_mapping_uuid):
volume_mapping = self.get_volume_mapping_by_uuid(
context, volume_mapping_uuid)
self.client.delete('/volume_mappings/' + volume_mapping.uuid)
def update_volume_mapping(self, context, volume_mapping_uuid, values):
if 'uuid' in values:
msg = _('Cannot overwrite UUID for an existing VolumeMapping.')
raise exception.InvalidParameterValue(err=msg)
try:
target_uuid = self.get_volume_mapping_by_uuid(
context, volume_mapping_uuid).uuid
target = self.client.read('/volume_mapping/' + target_uuid)
target_value = json.loads(target.value)
target_value.update(values)
target.value = json.dump_as_bytes(target_value)
self.client.update(target)
except etcd.EtcdKeyNotFound:
raise exception.VolumeMappingNotFound(
volume_mapping=volume_mapping_uuid)
except Exception as e:
LOG.error('Error occurred while updating volume mappping: %s',
six.text_type(e))
raise
return translate_etcd_result(target, 'volume_mapping')

View File

@ -290,3 +290,25 @@ class PciDevice(Base):
@classmethod
def fields(cls):
return cls._fields
class VolumeMapping(Base):
"""Represents a VolumeMapping."""
_path = '/volume_mapping'
_fields = objects.VolumeMapping.fields.keys()
def __init__(self, volume_mapping_data):
self.path = VolumeMapping.path()
for f in VolumeMapping.fields():
setattr(self, f, None)
self.id = 1
self.update(volume_mapping_data)
@classmethod
def path(cls):
return cls._path
@classmethod
def fields(cls):
return cls._fields

View File

@ -274,41 +274,42 @@ class Connection(object):
value=values['uuid'])
return volume_mapping
def get_volume_mapping_by_uuid(self, context, vm_uuid):
def get_volume_mapping_by_uuid(self, context, volume_mapping_uuid):
query = model_query(models.VolumeMapping)
query = self._add_tenant_filters(context, query)
query = query.filter_by(uuid=vm_uuid)
query = query.filter_by(uuid=volume_mapping_uuid)
try:
return query.one()
except NoResultFound:
raise exception.VolumeMappingNotFound(vm_uuid)
raise exception.VolumeMappingNotFound(volume_mapping_uuid)
def destroy_volume_mapping(self, context, vm_id):
def destroy_volume_mapping(self, context, volume_mapping_uuid):
session = get_session()
with session.begin():
query = model_query(models.VolumeMapping, session=session)
query = add_identity_filter(query, vm_id)
query = add_identity_filter(query, volume_mapping_uuid)
count = query.delete()
if count != 1:
raise exception.VolumeMappingNotFound(vm_id)
raise exception.VolumeMappingNotFound(
volume_mapping_uuid)
def update_volume_mapping(self, context, vm_id, values):
def update_volume_mapping(self, context, volume_mapping_uuid, values):
# NOTE(dtantsur): this can lead to very strange errors
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing VolumeMapping.")
raise exception.InvalidParameterValue(err=msg)
return self._do_update_volume_mapping(vm_id, values)
return self._do_update_volume_mapping(volume_mapping_uuid, values)
def _do_update_volume_mapping(self, vm_id, values):
def _do_update_volume_mapping(self, volume_mapping_uuid, values):
session = get_session()
with session.begin():
query = model_query(models.VolumeMapping, session=session)
query = add_identity_filter(query, vm_id)
query = add_identity_filter(query, volume_mapping_uuid)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.VolumeMappingNotFound(vm_id)
raise exception.VolumeMappingNotFound(volume_mapping_uuid)
ref.update(values)
return ref

View File

@ -10,6 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import etcd
from etcd import Client as etcd_client
import mock
from oslo_config import cfg
from oslo_utils import uuidutils
import six
@ -19,6 +24,8 @@ import zun.conf
from zun.db import api as dbapi
from zun.tests.unit.db import base
from zun.tests.unit.db import utils
from zun.tests.unit.db.utils import FakeEtcdMultipleResult
from zun.tests.unit.db.utils import FakeEtcdResult
CONF = zun.conf.CONF
@ -149,3 +156,177 @@ class DbVolumeMappingTestCase(base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue,
dbapi.update_volume_mapping, self.context,
volume_mapping.id, {'uuid': ''})
class EtcdDbVolumeMappingTestCase(base.DbTestCase):
def setUp(self):
cfg.CONF.set_override('db_type', 'etcd')
super(EtcdDbVolumeMappingTestCase, self).setUp()
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_create_volume_mapping(self, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
utils.create_test_volume_mapping(context=self.context)
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_create_volume_mapping_already_exists(self, mock_write,
mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
utils.create_test_volume_mapping(context=self.context)
mock_read.side_effect = lambda *args: None
self.assertRaises(exception.ResourceExists,
utils.create_test_volume_mapping,
context=self.context)
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_get_volume_mapping_by_uuid(self, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
volume_mapping = utils.create_test_volume_mapping(
context=self.context)
mock_read.side_effect = lambda *args: FakeEtcdResult(
volume_mapping.as_dict())
res = dbapi.get_volume_mapping_by_uuid(self.context,
volume_mapping.uuid)
self.assertEqual(volume_mapping.id, res.id)
self.assertEqual(volume_mapping.uuid, res.uuid)
@mock.patch.object(etcd_client, 'read')
def test_get_volume_mapping_that_does_not_exist(self, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
self.assertRaises(exception.VolumeMappingNotFound,
dbapi.get_volume_mapping_by_uuid,
self.context,
uuidutils.generate_uuid())
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_list_volume_mappings(self, mock_write, mock_read):
uuids = []
volume_mappings = []
mock_read.side_effect = etcd.EtcdKeyNotFound
for i in range(0, 6):
volume_mapping = utils.create_test_volume_mapping(
uuid=uuidutils.generate_uuid(),
context=self.context,
name='volume_mapping' + str(i))
volume_mappings.append(volume_mapping.as_dict())
uuids.append(six.text_type(volume_mapping['uuid']))
mock_read.side_effect = lambda *args: FakeEtcdMultipleResult(
volume_mappings)
res = dbapi.list_volume_mappings(self.context)
res_uuids = [r.uuid for r in res]
self.assertEqual(sorted(uuids), sorted(res_uuids))
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_list_volume_mappings_sorted(self, mock_write, mock_read):
uuids = []
volume_mappings = []
mock_read.side_effect = etcd.EtcdKeyNotFound
for i in range(0, 6):
volume_mapping = utils.create_test_volume_mapping(
uuid=uuidutils.generate_uuid(),
context=self.context,
name='volume_mapping' + str(i))
volume_mappings.append(volume_mapping.as_dict())
uuids.append(six.text_type(volume_mapping['uuid']))
mock_read.side_effect = lambda *args: FakeEtcdMultipleResult(
volume_mappings)
res = dbapi.list_volume_mappings(self.context, sort_key='uuid')
res_uuids = [r.uuid for r in res]
self.assertEqual(sorted(uuids), res_uuids)
self.assertRaises(exception.InvalidParameterValue,
dbapi.list_volume_mappings,
self.context,
sort_key='wrong_key')
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_list_volume_mappings_with_filters(self, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
volume_mapping1 = utils.create_test_volume_mapping(
name='volume_mapping1',
uuid=uuidutils.generate_uuid(),
context=self.context)
volume_mapping2 = utils.create_test_volume_mapping(
name='volume_mapping2',
uuid=uuidutils.generate_uuid(),
context=self.context,)
mock_read.side_effect = lambda *args: FakeEtcdMultipleResult(
[volume_mapping1.as_dict(), volume_mapping2.as_dict()])
res = dbapi.list_volume_mappings(
self.context, filters={'uuid': volume_mapping1.uuid})
self.assertEqual([volume_mapping1.id], [r.id for r in res])
res = dbapi.list_volume_mappings(
self.context, filters={'uuid': volume_mapping2.uuid})
self.assertEqual([volume_mapping2.id], [r.id for r in res])
res = dbapi.list_volume_mappings(
self.context, filters={'uuid': 'unknow-uuid'})
self.assertEqual([], [r.id for r in res])
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
@mock.patch.object(etcd_client, 'delete')
def test_destroy_volume_mapping_by_uuid(self, mock_delete,
mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
volume_mapping = utils.create_test_volume_mapping(
context=self.context)
mock_read.side_effect = lambda *args: FakeEtcdResult(
volume_mapping.as_dict())
dbapi.destroy_volume_mapping(self.context, volume_mapping.uuid)
mock_delete.assert_called_once_with(
'/volume_mappings/%s' % volume_mapping.uuid)
@mock.patch.object(etcd_client, 'read')
def test_destroy_volume_mapping_that_does_not_exist(self, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
self.assertRaises(exception.VolumeMappingNotFound,
dbapi.destroy_volume_mapping, self.context,
uuidutils.generate_uuid())
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
@mock.patch.object(etcd_client, 'update')
def test_update_volume_mapping(self, mock_update, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
volume_mapping = utils.create_test_volume_mapping(
context=self.context)
new_conn_info = 'new-conn-info'
mock_read.side_effect = lambda *args: FakeEtcdResult(
volume_mapping.as_dict())
dbapi.update_volume_mapping(self.context, volume_mapping.uuid,
{'container_info': new_conn_info})
self.assertEqual(new_conn_info, json.loads(
mock_update.call_args_list[0][0][0].value.decode('utf-8'))
['container_info'])
@mock.patch.object(etcd_client, 'read')
def test_update_volume_mapping_not_found(self, mock_read):
volume_mapping_uuid = uuidutils.generate_uuid()
new_conn_info = 'new-conn-info'
mock_read.side_effect = etcd.EtcdKeyNotFound
self.assertRaises(exception.VolumeMappingNotFound,
dbapi.update_volume_mapping, self.context,
volume_mapping_uuid,
{'container_info': new_conn_info})
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_update_volume_mapping_uuid(self, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
volume_mapping = utils.create_test_volume_mapping(
context=self.context)
self.assertRaises(exception.InvalidParameterValue,
dbapi.update_volume_mapping, self.context,
volume_mapping.uuid, {'uuid': ''})