Environment Template entity and its service

Change-Id: Ic46463e9e4900e2b9e3cf7aab4b9c5893da24344
This commit is contained in:
Henar Muñoz Frutos 2015-02-04 16:13:39 +01:00
parent d24a7bc397
commit 47351c5dac
9 changed files with 755 additions and 1 deletions

View File

@ -0,0 +1,59 @@
# 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.
"""
Create the environment-template table for the environment
template functionality.
Revision ID: 005
Revises: table template
"""
# revision identifiers, used by Alembic.
revision = '005'
down_revision = '004'
from alembic import op
import sqlalchemy as sa
MYSQL_ENGINE = 'InnoDB'
MYSQL_CHARSET = 'utf8'
def upgrade():
"""It creates the table environment-template. The name
plus the tenant_id should be unique in the table, since each
tenant cannot duplicate template names.
"""
op.create_table(
'environment-template',
sa.Column('created', sa.DateTime(), nullable=False),
sa.Column('updated', sa.DateTime(), nullable=False),
sa.Column('id', sa.String(length=255), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('tenant_id', sa.String(length=36), nullable=False),
sa.Column('version', sa.BigInteger(), nullable=False),
sa.Column('description', sa.Text(), nullable=False),
sa.Column('networking', sa.Text(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('tenant_id', 'name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
def downgrade():
op.drop_table('environment-template')

View File

@ -78,6 +78,26 @@ class Environment(Base, TimestampMixin):
return dictionary
class EnvironmentTemplate(Base, TimestampMixin):
"""Represents a Environment emplate in the metadata-store."""
__tablename__ = 'environment-template'
id = sa.Column(sa.String(36),
primary_key=True,
default=uuidutils.generate_uuid)
name = sa.Column(sa.String(255), nullable=False)
tenant_id = sa.Column(sa.String(36), nullable=False)
version = sa.Column(sa.BigInteger, nullable=False, default=0)
description = sa.Column(st.JsonBlob(), nullable=False, default={})
networking = sa.Column(st.JsonBlob(), nullable=True, default={})
def to_dict(self):
dictionary = super(EnvironmentTemplate, self).to_dict()
if 'description' in dictionary:
del dictionary['description']
return dictionary
class Session(Base, TimestampMixin):
__tablename__ = 'session'

View File

@ -11,13 +11,19 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import types
from oslo.utils import timeutils
from webob import exc
from murano.common.i18n import _
from murano.common import utils
from murano.db.services import environment_templates as env_temp
from murano.db.services import environments as envs
from murano.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class CoreServices(object):
@ -62,6 +68,96 @@ class CoreServices(object):
return result
@staticmethod
def get_template_data(env_template_id, path):
"""It obtains the data for the template. It includes
all the services. In case the path includes information
such as the env_template_id, the information provided will
be related to the entity specified in the path
:param env_template_id: The env_template_id to obtain the data
:param path: Id of service for which we checking status.
:return: The template description
"""
temp_description = env_temp.EnvTemplateServices.\
get_description(env_template_id)
if temp_description is None:
return None
if 'services' not in temp_description:
return []
result = utils.TraverseHelper.get(path, temp_description)
if result is None:
msg = _('Environment Template <EnvId {0}> is not found').format(
env_template_id)
LOG.error(msg)
raise exc.HTTPNotFound(explanation=msg)
return result
@staticmethod
def post_env_template_data(env_template_id, data, path):
"""It stores the template data inside the template
description.
:param env_template_id: The env_template_id to obtain the data
:param data: the template description
:param path: Id of service for which we checking status.
:return: The template description
"""
get_description = env_temp.EnvTemplateServices.get_description
save_description = env_temp.EnvTemplateServices.save_description
temp_description = get_description(env_template_id)
if temp_description is None:
msg = _('Environment Template <EnvId {0}> is not found').format(
env_template_id)
LOG.error(msg)
raise exc.HTTPNotFound(explanation=msg)
if 'services' not in temp_description:
temp_description['services'] = []
if path == '/services':
if isinstance(data, types.ListType):
utils.TraverseHelper.extend(path, data, temp_description)
else:
utils.TraverseHelper.insert(path, data, temp_description)
save_description(temp_description)
return data
@staticmethod
def post_application_data(env_template_id, data, path):
"""It stores the application data inside the template
description.
:param env_template_id: The env_template_id to obtain the data
:param data: the template description
:param path: Id of service for which we checking status.
:return: The template description
"""
get_description = env_temp.EnvTemplateServices.get_description
save_description = env_temp.EnvTemplateServices.save_description
temp_description = get_description(env_template_id)
if temp_description is None:
msg = _('Environment Template <EnvId {0}> is not found').format(
env_template_id)
LOG.error(msg)
raise exc.HTTPNotFound(explanation=msg)
if 'services' not in temp_description:
temp_description['services'] = []
if path == '/services':
if isinstance(data, types.ListType):
utils.TraverseHelper.extend(path, data, temp_description)
else:
utils.TraverseHelper.insert(path, data, temp_description)
save_description(temp_description, env_template_id)
return data
@staticmethod
def post_data(environment_id, session_id, data, path):
get_description = envs.EnvironmentServices.get_environment_description
@ -70,7 +166,11 @@ class CoreServices(object):
env_description = get_description(environment_id, session_id)
if env_description is None:
raise exc.HTTPMethodNotAllowed
msg = _('Environment <EnvId {0}> is not found').format(
environment_id)
LOG.error(msg)
raise exc.HTTPNotFound(explanation=msg)
if 'services' not in env_description:
env_description['services'] = []
@ -109,3 +209,22 @@ class CoreServices(object):
utils.TraverseHelper.remove(path, env_description)
save_description(session_id, env_description)
@staticmethod
def delete_env_template_data(env_template_id, path):
"""It deletes a template.
:param env_template_id: The env_template_id to be deleted.
:param path: The path to check.
"""
get_description = env_temp.EnvTemplateServices.get_description
save_description = env_temp.EnvTemplateServices.save_description
tmp_description = get_description(env_template_id)
if tmp_description is None:
msg = _('Environment Template <EnvId {0}> is not found').format(
env_template_id)
LOG.error(msg)
raise exc.HTTPNotFound(explanation=msg)
utils.TraverseHelper.remove(path, tmp_description)
save_description(tmp_description, env_template_id)

View File

@ -0,0 +1,167 @@
# Copyright (c) 2015 Telefonica I+D.
#
# 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.
from murano.common import uuidutils
from murano.db import models
from murano.db import session as db_session
from murano.openstack.common import log as logging
from oslo.db import exception as db_exc
LOG = logging.getLogger(__name__)
class EnvTemplateServices(object):
@staticmethod
def get_env_templates_by(filters):
"""Returns list of environment-templates.
:param filters: property filters
:return: Returns list of environment-templates
"""
unit = db_session.get_session()
templates = unit.query(models.EnvironmentTemplate). \
filter_by(**filters).all()
return templates
@staticmethod
def create(env_template_params, tenant_id):
"""Creates environment-template with specified params, in particular - name.
:param env_template_params: Dict, e.g. {'name': 'temp-name'}
:param tenant_id: Tenant Id
:return: Created Template
"""
env_template_params['id'] = uuidutils.generate_uuid()
env_template_params['tenant_id'] = tenant_id
env_template = models.EnvironmentTemplate()
env_template.update(env_template_params)
unit = db_session.get_session()
with unit.begin():
try:
unit.add(env_template)
except db_exc.DBDuplicateEntry:
msg = 'Environment template specified name already exists'
LOG.exception(msg)
raise db_exc.DBDuplicateEntry(explanation=msg)
env_template.update({'description': env_template_params})
env_template.save(unit)
return env_template
@staticmethod
def delete(env_template_id):
"""Deletes template.
:param env_template_id: Template that is going to be deleted
"""
env_temp_description = EnvTemplateServices.get_description(
env_template_id)
env_temp_description['description'] = None
EnvTemplateServices.save_description(
env_temp_description, env_template_id)
@staticmethod
def remove(env_template_id):
"""It deletes the environment template from database.
:param env_template_id: Template Id to be deleted.
"""
unit = db_session.get_session()
template = unit.query(models.EnvironmentTemplate).get(env_template_id)
if template:
with unit.begin():
unit.delete(template)
@staticmethod
def update(env_template_id, body):
"""It updates the description of a environment template.
:param env_template_id: Template Id to be deleted.
:param body: The description to be updated.
:return the template description updated
"""
unit = db_session.get_session()
template = unit.query(models.EnvironmentTemplate).get(env_template_id)
template.update(body)
template.save(unit)
return template
@staticmethod
def get_description(env_template_id):
"""Returns environment template description for specified template.
:param env_template_id: Template Id
:return: environment-template Description Object
"""
template = EnvTemplateServices.get_env_template(env_template_id)
if template is None:
raise ValueError("The environment template does not exists")
return template.description
@staticmethod
def get_application_description(env_template_id):
"""Returns environment template description for specified applications.
:param env_template_id: Template Id
:return: Template Description Object
"""
env_temp_desc = EnvTemplateServices.get_description(env_template_id)
if "services" not in env_temp_desc:
return []
else:
return env_temp_desc['services']
@staticmethod
def save_description(env_template_des, env_template_id=None):
"""Saves environment template description to specified session.
:param env_template_des: Template Description
:param env_template_id: The template ID.
"""
unit = db_session.get_session()
template = unit.query(models.EnvironmentTemplate).get(env_template_id)
template.update({'description': env_template_des})
template.save(unit)
@staticmethod
def env_template_exist(env_template_id):
"""It checks if the environment template exits in database.
:param env_template_id: The template ID
"""
template = EnvTemplateServices.get_env_template(env_template_id)
if template is None:
return False
else:
return True
@staticmethod
def get_env_template(env_template_id):
"""It obtains the environment template information from the database.
:param env_template_id: The template ID
"""
session = db_session.get_session()
return session.query(models.EnvironmentTemplate).get(env_template_id)

View File

@ -129,6 +129,12 @@ class MuranoMigrationsCheckers(object):
self.assertColumnExists(engine, 'task', 'action')
self.assertColumnExists(engine, 'status', 'task_id')
def _check_005(self, engine, data):
self.assertEqual('005', migration.version(engine))
self.assertColumnExists(engine, 'environment-template', 'id')
self.assertColumnExists(engine, 'environment-template', 'tenant_id')
self.assertColumnExists(engine, 'environment-template', 'name')
class TestMigrationsMySQL(MuranoMigrationsCheckers,
base.BaseWalkMigrationTestCase,

View File

@ -0,0 +1,166 @@
# Copyright (c) 2015 Telefonica I+D.
# 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.
import fixtures
class EmptyEnvironmentTemplateFixture(fixtures.Fixture):
def setUp(self):
super(EmptyEnvironmentTemplateFixture, self).setUp()
self.environment_template_desc = {
"tenant_id": "tenant_id",
"name": "my_template",
"id": "template_id"
}
self.addCleanup(delattr, self, 'environment_template_desc')
class AppEnvTemplateFixture(fixtures.Fixture):
def setUp(self):
super(AppEnvTemplateFixture, self).setUp()
self.env_template_desc = \
{
"services": [
{
"instance": {
"assignFloatingIp": "true",
"keyname": "mykeyname",
"image": "cloud-fedora-v3",
"flavor": "m1.medium",
"?": {
"type": "io.murano.resources.LinuxInstance",
"id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e"
}
},
"name": "orion",
"?":
{
"_26411a1861294160833743e45d0eaad9": {
"name": "tomcat"
},
"type": "io.murano.apps.apache.Tomcat",
"id": "tomcat_id"
},
"port": "8080"
}, {
"instance": "ef984a74-29a4-45c0-b1dc-2ab9f075732e",
"password": "XXX", "name":
"mysql",
"?": {
"_26411a1861294160833743e45d0eaad9": {
"name": "mysql"
},
"type": "io.murano.apps.database.MySQL",
"id": "54aaa43d-5970"
}
}
],
"tenant_id": "tenant_id",
"name": "template_name",
'id': 'template_id'
}
self.addCleanup(delattr, self, 'env_template_desc')
class ApplicationsFixture(fixtures.Fixture):
def setUp(self):
super(ApplicationsFixture, self).setUp()
self.applications_desc = [
{
"instance": {
"assignFloatingIp": "true",
"keyname": "mykeyname",
"image": "cloud-fedora-v3",
"flavor": "m1.medium",
"?": {
"type": "io.murano.resources.LinuxInstance",
"id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e"
}
},
"name": "orion",
"?":
{
"_26411a1861294160833743e45d0eaad9": {
"name": "tomcat"
},
"type": "io.murano.apps.apache.Tomcat",
"id": "tomcat_id"
},
"port": "8080"
},
{
"instance": "ef984a74-29a4-45c0-b1dc-2ab9f075732e",
"password": "XXX", "name":
"mysql",
"?": {
"_26411a1861294160833743e45d0eaad9": {
"name": "mysql"
},
"type": "io.murano.apps.database.MySQL",
"id": "54aaa43d-5970"
}
}
]
self.addCleanup(delattr, self, 'applications_desc')
class ApplicationTomcatFixture(fixtures.Fixture):
def setUp(self):
super(ApplicationTomcatFixture, self).setUp()
self.application_tomcat_desc = {
"instance": {
"assignFloatingIp": "true",
"keyname": "mykeyname",
"image": "cloud-fedora-v3",
"flavor": "m1.medium",
"?": {
"type": "io.murano.resources.LinuxInstance",
"id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e"
}
},
"name": "orion",
"?":
{
"_26411a1861294160833743e45d0eaad9": {
"name": "tomcat"
},
"type": "io.murano.apps.apache.Tomcat",
"id": "tomcat_id"
},
"port": "8080"
}
self.addCleanup(delattr, self, 'application_tomcat_desc')
class ApplicationMysqlFixture(fixtures.Fixture):
def setUp(self):
super(ApplicationMysqlFixture, self).setUp()
self.application_mysql_desc = {
"instance": "ef984a74-29a4-45c0-b1dc-2ab9f075732e",
"password": "XXX", "name":
"mysql",
"?": {
"_26411a1861294160833743e45d0eaad9": {
"name": "mysql"
},
"type": "io.murano.apps.database.MySQL",
"id": "54aaa43d-5970"
}
}
self.addCleanup(delattr, self, 'application_mysql_desc')

View File

@ -0,0 +1,134 @@
# Copyright (c) 2015 Telefonica I+D.
# 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.
import mock
from webob import exc
from murano.db.services import core_services
from murano.tests.unit import base
from murano.tests.unit.db.services import environment_templates as et
class TestCoreServices(base.MuranoTestCase):
def setUp(self):
super(TestCoreServices, self).setUp()
self.core_services = core_services.CoreServices
self.addCleanup(mock.patch.stopall)
@mock.patch('murano.db.services.environment_templates.EnvTemplateServices')
def test_empty_template(self, template_services_mock):
"""Check obtaining the template description without services."""
fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture())
template_services_mock.get_description.return_value = \
fixture.environment_template_desc
template_des = self.core_services.get_template_data('any', '/services')
self.assertEqual(template_des, [])
template_services_mock.get_description.assert_is_called()
template_services_mock.get_description.assert_called_with('any')
@mock.patch('murano.db.services.environment_templates.EnvTemplateServices')
def test_template_services(self, template_services_mock):
"""Check obtaining the template description with services."""
fixture_apps = self.useFixture(et.ApplicationsFixture())
fixture_env_apps = self.useFixture(et.AppEnvTemplateFixture())
template_services_mock.get_description.return_value = \
fixture_env_apps.env_template_desc
template_des = self.core_services.get_template_data('any', '/services')
self.assertEqual(template_des, fixture_apps.applications_desc)
template_services_mock.get_description.assert_is_called()
template_services_mock.get_description.assert_called_with('any')
@mock.patch('murano.db.services.environment_templates.EnvTemplateServices')
def test_template_get_service(self, template_services_mock):
"""Check obtaining the service description."""
fixture = self.useFixture(et.AppEnvTemplateFixture())
fixture2 = self.useFixture(et.ApplicationTomcatFixture())
template_services_mock.get_description.return_value = \
fixture.env_template_desc
template_des = \
self.core_services.get_template_data('any',
'/services/tomcat_id')
self.assertEqual(template_des, fixture2.application_tomcat_desc)
template_services_mock.get_description.assert_is_called()
template_services_mock.get_description.assert_called_with('any')
@mock.patch('murano.db.services.environment_templates.EnvTemplateServices')
def test_template_post_services(self, template_services_mock):
"""Check adding a service to a template."""
fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture())
fixture2 = self.useFixture(et.AppEnvTemplateFixture())
template_services_mock.get_description.return_value = \
fixture.environment_template_desc
template_des = self.core_services.\
post_env_template_data('any',
fixture2.env_template_desc,
'/services')
self.assertEqual(template_des, fixture2.env_template_desc)
template_services_mock.get_description.assert_is_called()
template_services_mock.\
get_description.assert_called_with('any')
@mock.patch('murano.db.services.environment_templates.EnvTemplateServices')
def test_template_delete_services(self, template_services_mock):
"""Check deleting a service in a template."""
fixture2 = self.useFixture(et.AppEnvTemplateFixture())
fixture = self.useFixture(et.ApplicationTomcatFixture())
template_services_mock.get_description.return_value = \
fixture2.env_template_desc
self.core_services.\
delete_env_template_data('any',
'/services/54aaa43d-5970')
template_des = self.core_services.get_template_data('any', '/services')
self.assertEqual(template_des, [fixture.application_tomcat_desc])
template_services_mock.get_description.assert_is_called()
template_services_mock.\
get_description.assert_called_with('any')
@mock.patch('murano.db.services.environment_templates.EnvTemplateServices')
def test_get_template_no_exists(self, template_services_mock):
"""Check obtaining a non-existing service."""
fixture2 = self.useFixture(et.AppEnvTemplateFixture())
template_services_mock.get_description.return_value = \
fixture2.env_template_desc
self.assertRaises(exc.HTTPNotFound,
self.core_services.get_template_data,
'any', '/services/noexists')
template_services_mock.get_description.assert_is_called()
template_services_mock.\
get_description.assert_called_with('any')
@mock.patch('murano.db.services.environment_templates.EnvTemplateServices')
def test_adding_services(self, template_services_mock):
"""Check adding services to a template."""
ftomcat = self.useFixture(et.ApplicationTomcatFixture())
fmysql = self.useFixture(et.ApplicationMysqlFixture())
fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture())
fservices = self.useFixture(et.ApplicationsFixture())
template_services_mock.get_description.return_value =\
fixture.environment_template_desc
self.core_services.\
post_env_template_data('any',
ftomcat.application_tomcat_desc,
'/services')
self.core_services.\
post_env_template_data('any',
fmysql.application_mysql_desc,
'/services')
template_des = \
self.core_services.get_template_data('any', '/services')
self.assertEqual(template_des, fservices.applications_desc)
template_services_mock.get_description.assert_is_called()
template_services_mock.get_description.assert_called_with('any')

View File

@ -0,0 +1,83 @@
# Copyright (c) 2015 Telefonica I+D.
# 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.
import fixtures
import mock
from murano.db.services import environment_templates as env_temp
from murano.tests.unit import base
from murano.tests.unit.db.services import environment_templates as et
class TestTemplateServices(base.MuranoWithDBTestCase,
fixtures.TestWithFixtures):
def setUp(self):
super(TestTemplateServices, self).setUp()
self.template_services = env_temp.EnvTemplateServices
self.uuids = ['template_id']
self.mock_uuid = self._stub_uuid(self.uuids)
self.addCleanup(mock.patch.stopall)
def test_create_template(self):
"""Check creating a template without services."""
fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture())
"""Check the creation of a template."""
body = {
"name": "my_template"
}
template_des = self.template_services.create(body, 'tenant_id')
self.assertEqual(template_des.description,
fixture.environment_template_desc)
def test_get_empty_template(self):
"""Check obtaining information about a template without services."""
fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture())
self.test_create_template()
template = \
self.template_services.get_description("template_id")
self.assertEqual(template, fixture.environment_template_desc)
def test_get_template_services(self):
"""Check obtaining information about a template with services."""
fixture = self.useFixture(et.AppEnvTemplateFixture())
template = self.template_services.create(fixture.env_template_desc,
'tenant_id')
self.assertEqual(template.description, fixture.env_template_desc)
template_des = \
self.template_services.get_description("template_id")
self.assertEqual(template_des, fixture.env_template_desc)
def test_get_template_no_exists(self):
"""Check obtaining information about a template which
does not exist.
"""
self.assertRaises(ValueError,
self.template_services.get_description,
'no_exists')
def test_delete_template(self):
"""Check deleting a template."""
self.test_create_template()
self.template_services.delete("template_id")
def _stub_uuid(self, values=[]):
class FakeUUID(object):
def __init__(self, v):
self.hex = v
mock_uuid4 = mock.patch('uuid.uuid4').start()
mock_uuid4.side_effect = [FakeUUID(v) for v in values]
return mock_uuid4