Add db api layer for CRUD operations

Add the DB layer Operations for server tags

- set/unset server tags
- get server tags
- add/delete single tag
- check whether server tag exists

Change-Id: I2338ece8e4ae880835f6e20ef1e9e71a228a9703
Implements: blueprint server-tags-support
This commit is contained in:
Tao Li 2017-10-11 10:50:38 +08:00
parent 57fe9d796f
commit ea83f4711a
7 changed files with 319 additions and 0 deletions

View File

@ -504,4 +504,8 @@ class NodeNotAllowedManaged(Forbidden):
_msg_fmt = _("The bare metal node %(node_uuid)s is not allowed to "
"be managed")
class ServerTagNotFound(NotFound):
_msg_fmt = _("Server %(server_id)s doesn't have a tag '%(tag)s'")
ObjectActionError = obj_exc.ObjectActionError

View File

@ -289,3 +289,70 @@ class Connection(object):
def server_group_members_add(self, context, group_uuid, members):
"""Add a list of members to a server group"""
return IMPL.server_group_members_add(context, group_uuid, members)
@abc.abstractmethod
def set_server_tags(self, context, server_id, tags):
"""Replace all of the server tags with specified list of tags.
This ignores duplicate tags in the specified list.
:param context: Request context
:param server_id: The id of a server.
:param tags: List of tags.
:returns: A list of ServerTag objects.
:raises: ServerNotFound if the server is not found.
"""
@abc.abstractmethod
def unset_server_tags(self, context, server_id):
"""Remove all tags of the server.
:param context: Request context
:param server_id: The id of a server.
:raises: ServerNotFound if the server is not found.
"""
@abc.abstractmethod
def get_server_tags_by_server_id(self, context, server_id):
"""Get server tags based on its id.
:param context: Request context
:param server_id: The id of a server.
:returns: A list of ServerTag objects.
:raises: ServerNotFound if the server is not found.
"""
@abc.abstractmethod
def add_server_tag(self, context, server_id, tag):
"""Add tag to the server.
If the server_id and tag pair already exists, this should still
succeed.
:param context: Request context
:param server_id: The id of a server.
:param tag: A tag string.
:returns: the ServerTag object.
:raises: ServerNotFound if the server is not found.
"""
@abc.abstractmethod
def delete_server_tag(self, context, server_id, tag):
"""Delete specified tag from the server.
:param context: Request context
:param server_id: The id of a server.
:param tag: A tag string.
:raises: ServerNotFound if the server is not found.
"""
@abc.abstractmethod
def server_tag_exists(self, context, server_id, tag):
"""Check if the specified tag exist on the server.
:param context: Request context
:param server_id: The id of a server.
:param tag: A tag string.
:returns: True if the tag exists otherwise False.
:raises: ServerTagNotFound if the tag is not found.
"""

View File

@ -234,6 +234,7 @@ class Connection(api.Connection):
nic_ref = models.ServerNic()
nic_ref.update(nic)
nic_refs.append(nic_ref)
with _session_for_write() as session:
try:
session.add(server)
@ -1075,6 +1076,70 @@ class Connection(api.Connection):
raise exception.ServerGroupNotFound(group_uuid=group_uuid)
self._server_group_members_add(context, group.id, members)
def _check_server_exists(self, context, server_id):
if not model_query(context, models.Server)\
.filter_by(id=server_id).scalar():
raise exception.ServerNotFound(server=server_id)
@oslo_db_api.retry_on_deadlock
def set_server_tags(self, context, server_id, tags):
# remove duplicate tags
tags = set(tags)
with _session_for_write() as session:
self.unset_server_tags(context, server_id)
server_tags = []
for tag in tags:
server_tag = models.ServerTag(tag=tag, server_id=server_id)
session.add(server_tag)
server_tags.append(server_tag)
return server_tags
@oslo_db_api.retry_on_deadlock
def unset_server_tags(self, context, server_id):
self._check_server_exists(context, server_id)
with _session_for_write():
model_query(context, models.ServerTag)\
.filter_by(server_id=server_id).delete()
def get_server_tags_by_server_id(self, context, server_id):
self._check_server_exists(context, server_id)
result = (model_query(context, models.ServerTag)
.filter_by(server_id=server_id)
.all())
return result
@oslo_db_api.retry_on_deadlock
def add_server_tag(self, context, server_id, tag):
self._check_server_exists(context, server_id)
server_tag = models.ServerTag(tag=tag, server_id=server_id)
try:
with _session_for_write() as session:
session.add(server_tag)
session.flush()
except db_exc.DBDuplicateEntry:
# NOTE(litao): ignore tags duplicates
pass
return server_tag
@oslo_db_api.retry_on_deadlock
def delete_server_tag(self, context, server_id, tag):
self._check_server_exists(context, server_id)
with _session_for_write():
result = model_query(context, models.ServerTag).filter_by(
server_id=server_id, tag=tag).delete()
if not result:
raise exception.ServerTagNotFound(server_id=server_id, tag=tag)
def server_tag_exists(self, context, server_id, tag):
self._check_server_exists(context, server_id)
q = model_query(context, models.ServerTag)\
.filter_by(server_id=server_id, tag=tag)
return q.scalar()
def _get_id_from_flavor_query(context, type_id):
return model_query(context, models.Flavors). \

View File

@ -385,3 +385,10 @@ class ServerTag(Base):
server_id = Column(Integer, ForeignKey('servers.id'),
primary_key=True, nullable=False)
tag = Column(String(255), primary_key=True, nullable=False)
server = orm.relationship(
"Server",
backref='tags',
primaryjoin='and_(ServerTag.server_id == Server.id)',
foreign_keys=server_id
)

View File

@ -0,0 +1,130 @@
# Copyright 2017 Fiberhome Integration Technologies Co.,LTD.
# All Rights Reserved.
#
# 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 Server tags via the DB API"""
from mogan.common import exception
from mogan.tests.unit.db import base
from mogan.tests.unit.db import utils
class DbServerTagTestCase(base.DbTestCase):
def setUp(self):
super(DbServerTagTestCase, self).setUp()
self.server = utils.create_test_server()
def test_set_server_tags(self):
tags = self.dbapi.set_server_tags(self.context, self.server.id,
['tag1', 'tag2'])
self.assertEqual(self.server.id, tags[0].server_id)
self.assertItemsEqual(['tag1', 'tag2'], [tag.tag for tag in tags])
tags = self.dbapi.set_server_tags(self.context, self.server.id, [])
self.assertEqual([], tags)
def test_set_server_tags_duplicate(self):
tags = self.dbapi.set_server_tags(self.context, self.server.id,
['tag1', 'tag2', 'tag2'])
self.assertEqual(self.server.id, tags[0].server_id)
self.assertItemsEqual(['tag1', 'tag2'], [tag.tag for tag in tags])
def test_set_server_tags_server_not_exist(self):
self.assertRaises(exception.ServerNotFound,
self.dbapi.set_server_tags,
self.context, '1234', ['tag1', 'tag2'])
def test_get_server_tags_by_server_id(self):
self.dbapi.set_server_tags(self.context, self.server.id,
['tag1', 'tag2'])
tags = self.dbapi.get_server_tags_by_server_id(self.context,
self.server.id)
self.assertEqual(self.server.id, tags[0].server_id)
self.assertItemsEqual(['tag1', 'tag2'], [tag.tag for tag in tags])
def test_get_server_tags_empty(self):
tags = self.dbapi.get_server_tags_by_server_id(self.context,
self.server.id)
self.assertEqual([], tags)
def test_get_server_tags_server_not_exist(self):
self.assertRaises(exception.ServerNotFound,
self.dbapi.get_server_tags_by_server_id,
self.context, '123')
def test_unset_server_tags(self):
self.dbapi.set_server_tags(self.context, self.server.id,
['tag1', 'tag2'])
self.dbapi.unset_server_tags(self.context, self.server.id)
tags = self.dbapi.get_server_tags_by_server_id(self.context,
self.server.id)
self.assertEqual([], tags)
def test_unset_empty_server_tags(self):
self.dbapi.unset_server_tags(self.context, self.server.id)
tags = self.dbapi.get_server_tags_by_server_id(self.context,
self.server.id)
self.assertEqual([], tags)
def test_unset_server_tags_server_not_exist(self):
self.assertRaises(exception.ServerNotFound,
self.dbapi.unset_server_tags, self.context, '123')
def test_add_server_tag(self):
tag = self.dbapi.add_server_tag(self.context, self.server.id, 'tag1')
self.assertEqual(self.server.id, tag.server_id)
self.assertEqual('tag1', tag.tag)
def test_add_server_tag_duplicate(self):
tag = self.dbapi.add_server_tag(self.context, self.server.id, 'tag1')
tag = self.dbapi.add_server_tag(self.context, self.server.id, 'tag1')
self.assertEqual(self.server.id, tag.server_id)
self.assertEqual('tag1', tag.tag)
def test_add_server_tag_server_not_exist(self):
self.assertRaises(exception.ServerNotFound,
self.dbapi.add_server_tag, self.context,
'123', 'tag1')
def test_delete_server_tag(self):
self.dbapi.set_server_tags(self.context, self.server.id,
['tag1', 'tag2'])
self.dbapi.delete_server_tag(self.context, self.server.id, 'tag1')
tags = self.dbapi.get_server_tags_by_server_id(self.context,
self.server.id)
self.assertEqual(1, len(tags))
self.assertEqual('tag2', tags[0].tag)
def test_delete_server_tag_not_found(self):
self.assertRaises(exception.ServerTagNotFound,
self.dbapi.delete_server_tag, self.context,
self.server.id, 'tag1')
def test_delete_server_tag_server_not_found(self):
self.assertRaises(exception.ServerNotFound,
self.dbapi.delete_server_tag, self.context,
'123', 'tag1')
def test_server_tag_exists(self):
self.dbapi.set_server_tags(self.context,
self.server.id, ['tag1', 'tag2'])
ret = self.dbapi.server_tag_exists(self.context,
self.server.id, 'tag1')
self.assertTrue(ret)
def test_server_tag_not_exists(self):
ret = self.dbapi.server_tag_exists(self.context,
self.server.id, 'tag1')
self.assertFalse(ret)

View File

@ -149,3 +149,25 @@ class DbServerTestCase(base.DbTestCase):
self.context,
server.uuid,
{'uuid': '12345678-9999-0000-aaaa-123456789012'})
def test_tags_get_destroyed_after_destroying_a_server(self):
server = utils.create_test_server(id=123)
tag = utils.create_test_server_tag(self.context, server_id=server.id)
self.assertTrue(self.dbapi.server_tag_exists(self.context,
server.id, tag.tag))
self.dbapi.server_destroy(self.context, server.id)
self.assertRaises(exception.ServerNotFound,
self.dbapi.server_tag_exists,
self.context, server.id, tag.tag)
def test_tags_get_destroyed_after_destroying_a_server_by_uuid(self):
server = utils.create_test_server(id=124)
tag = utils.create_test_server_tag(self.context, server_id=server.id)
self.assertTrue(self.dbapi.server_tag_exists(self.context,
server.id, tag.tag))
self.dbapi.server_destroy(self.context, server.uuid)
self.assertRaises(exception.ServerNotFound,
self.dbapi.server_tag_exists,
self.context, server.id, tag.tag)

View File

@ -243,3 +243,27 @@ def create_test_server_group(context={}, **kw):
policies = server_fault.pop('policies')
return dbapi.server_group_create(context, server_fault, policies=policies,
members=members)
def get_test_server_tag(**kw):
return {
"tag": kw.get("tag", "tag1"),
"server_id": kw.get("server_id", "123"),
'created_at': kw.get('created_at'),
'updated_at': kw.get('updated_at'),
}
def create_test_server_tag(context, **kw):
"""Create test node tag entry in DB and return NodeTag DB object.
Function to be used to create test NodeTag objects in the database.
:param context: Request context
:param kw: kwargs with overriding values for tag's attributes.
:returns: Test NodeTag DB object.
"""
tag = get_test_server_tag(**kw)
dbapi = db_api.get_instance()
return dbapi.add_server_tag(context, tag['server_id'], tag['tag'])