Merge "Add etcd db for VolumeMapping"
This commit is contained in:
commit
9d6fe8870d
@ -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')
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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': ''})
|
||||
|
Loading…
x
Reference in New Issue
Block a user