Merge "Imply the etcd api of capsule"

This commit is contained in:
Zuul 2017-12-04 03:06:09 +00:00 committed by Gerrit Code Review
commit 8154d6749c
3 changed files with 451 additions and 3 deletions

View File

@ -765,7 +765,7 @@ def create_capsule(context, values):
@profiler.trace("db")
def get_capsule_by_uuid(context, capsule_uuid):
"""Return a container.
"""Return a capsule.
:param context: The security context
:param capsule_uuid: The uuid of a capsule.
@ -789,7 +789,7 @@ def get_capsule_by_meta_name(context, capsule_name):
@profiler.trace("db")
def destroy_capsule(context, capsule_id):
"""Destroy a container and all associated interfaces.
"""Destroy a capsule and all associated interfaces.
:param context: Request context
:param capsule_id: The id or uuid of a capsule.
@ -799,7 +799,7 @@ def destroy_capsule(context, capsule_id):
@profiler.trace("db")
def update_capsule(context, capsule_id, values):
"""Update properties of a container.
"""Update properties of a capsule.
:context: Request context
:param container_id: The id or uuid of a capsule.

View File

@ -79,6 +79,8 @@ def translate_etcd_result(etcd_result, model_type):
ret = models.ResourceClass(data)
elif model_type == 'compute_node':
ret = models.ComputeNode(data)
elif model_type == 'capsule':
ret = models.Capsule(data)
else:
raise exception.InvalidParameterValue(
_('The model_type value: %s is invalid.'), model_type)
@ -638,3 +640,107 @@ class EtcdAPI(object):
compute_nodes = self._filter_resources(compute_nodes, filters)
return self._process_list_result(compute_nodes, limit=limit,
sort_key=sort_key)
def list_capsules(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None):
try:
res = getattr(self.client.read('/capsules'), 'children', None)
except etcd.EtcdKeyNotFound:
# Before the first container been created, path '/capsules'
# does not exist.
return []
except Exception as e:
LOG.error(
"Error occurred while reading from etcd server: %s",
six.text_type(e))
raise
capsules = []
for c in res:
if c.value is not None:
capsules.append(translate_etcd_result(c, 'capsule'))
filters = self._add_tenant_filters(context, filters)
filtered_capsules = self._filter_resources(
capsules, filters)
return self._process_list_result(filtered_capsules,
limit=limit, sort_key=sort_key)
@lockutils.synchronized('etcd_capsule')
def create_capsule(self, context, values):
# ensure defaults are present for new capsules
if not values.get('uuid'):
values['uuid'] = uuidutils.generate_uuid()
capsule = models.Capsule(values)
try:
capsule.save()
except Exception:
raise
return capsule
def get_capsule_by_uuid(self, context, capsule_uuid):
try:
res = self.client.read('/capsules/' + capsule_uuid)
capsule = translate_etcd_result(res, 'capsule')
filtered_capsules = self._filter_resources(
[capsule], self._add_tenant_filters(context, {}))
if len(filtered_capsules) > 0:
return filtered_capsules[0]
else:
raise exception.CapsuleNotFound(capsule=capsule_uuid)
except etcd.EtcdKeyNotFound:
raise exception.CapsuleNotFound(capsule=capsule_uuid)
except Exception as e:
LOG.error('Error occurred while retrieving capsule: %s',
six.text_type(e))
raise
def get_capsule_by_meta_name(self, context, capsule_meta_name):
try:
filters = self._add_tenant_filters(
context, {'meta_name': capsule_meta_name})
capsules = self.list_capsules(context, filters=filters)
except etcd.EtcdKeyNotFound:
raise exception.CapsuleNotFound(capsule=capsule_meta_name)
except Exception as e:
LOG.error('Error occurred while retrieving capsule: %s',
six.text_type(e))
raise
if len(capsules) > 1:
raise exception.Conflict('Multiple capsules exist with same '
'meta name. Please use the capsule uuid '
'instead.')
elif len(capsules) == 0:
raise exception.CapsuleNotFound(capsule=capsule_meta_name)
return capsules[0]
@lockutils.synchronized('etcd_capsule')
def destroy_capsule(self, context, capsule_id):
capsule = self.get_capsule_by_uuid(context, capsule_id)
self.client.delete('/capsules/' + capsule.uuid)
@lockutils.synchronized('etcd_capsule')
def update_capsule(self, context, capsule_id, values):
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing Capsule.")
raise exception.InvalidParameterValue(err=msg)
try:
target_uuid = self.get_capsule_by_uuid(
context, capsule_id).uuid
target = self.client.read('/capsules/' + 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.CapsuleNotFound(capsule=capsule_id)
except Exception as e:
LOG.error('Error occurred while updating capsule: %s',
six.text_type(e))
raise
return translate_etcd_result(target, 'capsule')

View File

@ -0,0 +1,342 @@
# 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.
"""Tests for manipulating Capsule via the DB API"""
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
from zun.common import exception
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
class SqlDbCapsuleTestCase(base.DbTestCase):
def setUp(self):
cfg.CONF.set_override('db_type', 'sql')
super(SqlDbCapsuleTestCase, self).setUp()
def test_create_capsule(self):
utils.create_test_capsule(context=self.context)
def test_create_capsule_already_exists(self):
utils.create_test_capsule(context=self.context)
self.assertRaises(exception.CapsuleAlreadyExists,
utils.create_test_capsule,
context=self.context)
def test_get_capsule_by_uuid(self):
capsule = utils.create_test_capsule(context=self.context)
res = dbapi.get_capsule_by_uuid(self.context,
capsule.uuid)
self.assertEqual(capsule.id, res.id)
self.assertEqual(capsule.uuid, res.uuid)
def test_get_capsule_by_meta_name(self):
capsule = utils.create_test_capsule(context=self.context)
res = dbapi.get_capsule_by_meta_name(self.context,
capsule.meta_name)
self.assertEqual(capsule.id, res.id)
self.assertEqual(capsule.meta_name, res.meta_name)
def test_get_non_exists_capsule(self):
self.assertRaises(exception.CapsuleNotFound,
dbapi.get_capsule_by_uuid,
self.context,
uuidutils.generate_uuid())
def test_list_capsules(self):
uuids = []
for i in range(1, 6):
capsule = utils.create_test_capsule(
uuid=uuidutils.generate_uuid(),
context=self.context,
name='capsule' + str(i)
)
uuids.append(six.text_type(capsule['uuid']))
res = dbapi.list_capsules(self.context)
res_uuids = [r.uuid for r in res]
self.assertEqual(sorted(uuids), sorted(res_uuids))
def test_list_capsules_sorted_with_valid_sort_key(self):
uuids = []
for i in range(1, 6):
capsule = utils.create_test_capsule(
uuid=uuidutils.generate_uuid(),
context=self.context,
name='capsule' + str(i)
)
uuids.append(six.text_type(capsule['uuid']))
res = dbapi.list_capsules(self.context, sort_key='uuid')
res_uuids = [r.uuid for r in res]
self.assertEqual(sorted(uuids), res_uuids)
def test_list_capsules_sorted_with_invalid_sort_key(self):
self.assertRaises(exception.InvalidParameterValue,
dbapi.list_capsules,
self.context,
sort_key='foo')
def test_list_capsules_with_filters(self):
capsule1 = utils.create_test_capsule(
name='capsule1',
uuid=uuidutils.generate_uuid(),
context=self.context)
capsule2 = utils.create_test_capsule(
name='capsule2',
uuid=uuidutils.generate_uuid(),
context=self.context)
res = dbapi.list_capsules(
self.context, filters={'uuid': capsule1.uuid})
self.assertEqual([capsule1.id], [r.id for r in res])
res = dbapi.list_capsules(
self.context, filters={'uuid': capsule2.uuid})
self.assertEqual([capsule2.id], [r.id for r in res])
res = dbapi.list_capsules(
self.context, filters={'uuid': 'unknow-uuid'})
self.assertEqual([], [r.id for r in res])
def test_destroy_capsule(self):
capsule = utils.create_test_capsule(context=self.context)
dbapi.destroy_capsule(self.context, capsule.id)
self.assertRaises(exception.CapsuleNotFound,
dbapi.get_capsule_by_uuid,
self.context,
capsule.uuid)
def test_destroy_capsule_by_uuid(self):
capsule = utils.create_test_capsule(context=self.context)
dbapi.destroy_capsule(self.context, capsule.uuid)
self.assertRaises(exception.CapsuleNotFound,
dbapi.get_capsule_by_uuid,
self.context,
capsule.uuid)
def test_destroy_non_exists_capsule(self):
self.assertRaises(exception.CapsuleNotFound,
dbapi.destroy_capsule,
self.context,
uuidutils.generate_uuid())
def test_update_capsule(self):
capsule = utils.create_test_capsule(context=self.context)
current_meta_name = capsule.meta_name
new_meta_name = 'new-meta-name'
self.assertNotEqual(current_meta_name, new_meta_name)
res = dbapi.update_capsule(self.context, capsule.id,
{'meta_name': new_meta_name})
self.assertEqual(new_meta_name, res.meta_name)
def test_update_capsule_not_found(self):
capsule_uuid = uuidutils.generate_uuid()
new_meta_name = 'new-meta-name'
self.assertRaises(exception.CapsuleNotFound,
dbapi.update_capsule,
self.context, capsule_uuid,
{'meta_name': new_meta_name})
def test_update_capsule_uuid(self):
capsule = utils.create_test_capsule(context=self.context)
self.assertRaises(exception.InvalidParameterValue,
dbapi.update_capsule, self.context,
capsule.id, {'uuid': ''})
class EtcdDbCapsuleTestCase(base.DbTestCase):
def setUp(self):
cfg.CONF.set_override('db_type', 'etcd')
super(EtcdDbCapsuleTestCase, self).setUp()
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_create_capsule(self, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
utils.create_test_capsule(context=self.context)
mock_read.side_effect = lambda *args: None
self.assertRaises(exception.ResourceExists,
utils.create_test_capsule,
context=self.context)
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_get_capsule_by_uuid(self, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
capsule = utils.create_test_capsule(context=self.context)
mock_read.side_effect = lambda *args: FakeEtcdResult(
capsule.as_dict())
res = dbapi.get_capsule_by_uuid(self.context,
capsule.uuid)
self.assertEqual(capsule.id, res.id)
self.assertEqual(capsule.uuid, res.uuid)
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_get_capsule_by_meta_name(self, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
capsule = utils.create_test_capsule(context=self.context)
mock_read.side_effect = lambda *args: FakeEtcdMultipleResult(
[capsule.as_dict()])
res = dbapi.get_capsule_by_meta_name(
self.context, capsule.meta_name)
self.assertEqual(capsule.id, res.id)
self.assertEqual(capsule.uuid, res.uuid)
@mock.patch.object(etcd_client, 'read')
def test_get_nonexists_capsule(self, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
self.assertRaises(exception.CapsuleNotFound,
dbapi.get_capsule_by_uuid,
self.context,
uuidutils.generate_uuid())
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_list_capsules(self, mock_write, mock_read):
uuids = []
capsules = []
mock_read.side_effect = etcd.EtcdKeyNotFound
for i in range(1, 6):
capsule = utils.create_test_capsule(
uuid=uuidutils.generate_uuid(),
context=self.context,
name='capsule' + str(i))
capsules.append(capsule.as_dict())
uuids.append(six.text_type(capsule['uuid']))
mock_read.side_effect = lambda *args: FakeEtcdMultipleResult(
capsules)
res = dbapi.list_capsules(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_capsules_sorted(self, mock_write,
mock_read):
uuids = []
capsules = []
mock_read.side_effect = etcd.EtcdKeyNotFound
for i in range(1, 6):
capsule = utils.create_test_capsule(
uuid=uuidutils.generate_uuid(),
context=self.context,
name='capsule' + str(i))
capsules.append(capsule.as_dict())
uuids.append(six.text_type(capsule['uuid']))
mock_read.side_effect = lambda *args: FakeEtcdMultipleResult(
capsules)
res = dbapi.list_capsules(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_capsules,
self.context,
sort_key='foo')
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_list_capsules_with_filters(self, mock_write,
mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
capsule1 = utils.create_test_capsule(
name='capsule1',
uuid=uuidutils.generate_uuid(),
context=self.context)
capsule2 = utils.create_test_capsule(
name='capsule2',
uuid=uuidutils.generate_uuid(),
context=self.context)
mock_read.side_effect = lambda *args: FakeEtcdMultipleResult(
[capsule1.as_dict(), capsule2.as_dict()])
res = dbapi.list_capsules(
self.context, filters={'uuid': capsule1.uuid})
self.assertEqual([capsule1.id], [r.id for r in res])
res = dbapi.list_capsules(
self.context, filters={'uuid': capsule2.uuid})
self.assertEqual([capsule2.id], [r.id for r in res])
res = dbapi.list_capsules(
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_capsule_by_uuid(self, mock_delete, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
capsule = utils.create_test_capsule(context=self.context)
mock_read.side_effect = lambda *args: FakeEtcdResult(
capsule.as_dict())
dbapi.destroy_capsule(self.context, capsule.uuid)
mock_delete.assert_called_once_with('/capsules/%s' % capsule.uuid)
@mock.patch.object(etcd_client, 'read')
def test_destroy_non_exists_capsule(self, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
self.assertRaises(exception.CapsuleNotFound,
dbapi.destroy_capsule, 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_capsule(self, mock_update, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
capsule = utils.create_test_capsule(context=self.context)
new_meta_name = 'new_meta_name'
mock_read.side_effect = lambda *args: FakeEtcdResult(
capsule.as_dict())
dbapi.update_capsule(self.context, capsule.uuid,
{'meta_name': new_meta_name})
self.assertEqual(new_meta_name, json.loads(
mock_update.call_args_list[0][0][0].value.decode('utf-8'))
['meta_name'])
@mock.patch.object(etcd_client, 'read')
def test_update_capsule_not_found(self, mock_read):
capsule_uuid = uuidutils.generate_uuid()
new_meta_name = 'new-meta-name'
mock_read.side_effect = etcd.EtcdKeyNotFound
self.assertRaises(exception.CapsuleNotFound,
dbapi.update_capsule, self.context,
capsule_uuid, {'meta_name': new_meta_name})
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
def test_update_capsule_uuid(self, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
capsule = utils.create_test_capsule(context=self.context)
self.assertRaises(exception.InvalidParameterValue,
dbapi.update_capsule, self.context,
capsule.uuid, {'uuid': ''})