diff --git a/murano/api/v1/router.py b/murano/api/v1/router.py index c6cba057..5479b6fd 100644 --- a/murano/api/v1/router.py +++ b/murano/api/v1/router.py @@ -117,6 +117,10 @@ class API(wsgi.Router): controller=templates_resource, action='delete', conditions={'method': ['DELETE']}) + mapper.connect('/templates/{env_template_id}/create-environment', + controller=templates_resource, + action='create_environment', + conditions={'method': ['POST']}) applications_resource = template_applications.create_resource() mapper.connect('/templates/{env_template_id}/services', diff --git a/murano/api/v1/templates.py b/murano/api/v1/templates.py index 76162818..04aefae9 100644 --- a/murano/api/v1/templates.py +++ b/murano/api/v1/templates.py @@ -15,13 +15,16 @@ from oslo.db import exception as db_exc from webob import exc -from murano.api.v1 import environments as envs +from murano.api.v1 import environments as envs_api from murano.api.v1 import request_statistics from murano.common.i18n import _ from murano.common import policy +from murano.common import utils from murano.common import wsgi from murano.db.services import core_services from murano.db.services import environment_templates as env_temps +from murano.db.services import environments as envs +from murano.db.services import sessions from murano.openstack.common import log as logging LOG = logging.getLogger(__name__) @@ -59,7 +62,7 @@ class Controller(object): policy.check('create_env_template', request.context) try: LOG.debug('ENV TEMP NAME: {0}>'.format(body['name'])) - if not envs.VALID_NAME_REGEX.match(str(body['name'])): + if not envs_api.VALID_NAME_REGEX.match(str(body['name'])): msg = _('Environment Template must contain only alphanumeric ' 'or "_-." characters, must start with alpha') LOG.exception(msg) @@ -114,7 +117,7 @@ class Controller(object): self._validate_request(request, env_template_id) try: LOG.debug('ENV TEMP NAME: {0}>'.format(body['name'])) - if not envs.VALID_NAME_REGEX.match(str(body['name'])): + if not envs_api.VALID_NAME_REGEX.match(str(body['name'])): msg = _('Env Template must contain only alphanumeric ' 'or "_-." characters, must start with alpha') LOG.exception(msg) @@ -141,6 +144,68 @@ class Controller(object): env_temps.EnvTemplateServices.remove(env_template_id) return + def has_services(self, template): + """"It checks if the template has services + :param template: the template to check. + :return: True or False + """ + if not template.description: + return False + + if (template.description.get('services')): + return True + return False + + @request_statistics.stats_count(API_NAME, 'Create_environment') + def create_environment(self, request, env_template_id, body): + """Creates environment and session from template. + :param request: operation request + :param env_template_id: environment template ID + :param body: the environment name + :return: session_id and environment_id + """ + LOG.debug('Templates:Create environment '. + format(env_template_id)) + target = {"env_template_id": env_template_id} + policy.check('create_environment', request.context, target) + + self._validate_request(request, env_template_id) + template = env_temps.EnvTemplateServices.\ + get_env_template(env_template_id) + + if ('name' not in body or + not envs_api.VALID_NAME_REGEX.match(str(body['name']))): + msg = _('Environment must contain only alphanumeric ' + 'or "_-." characters, must start with alpha') + LOG.error(msg) + raise exc.HTTPBadRequest(explanation=msg) + LOG.debug('ENVIRONMENT NAME: {0}>'.format(body['name'])) + + try: + environment = envs.EnvironmentServices.create( + body.copy(), request.context.tenant) + except db_exc.DBDuplicateEntry: + msg = _('Environment with specified name already exists') + LOG.exception(msg) + raise exc.HTTPConflict(explanation=msg) + + user_id = request.context.user + session = sessions.SessionServices.create(environment.id, user_id) + + if self.has_services(template): + services_node = utils.TraverseHelper.get("services", + template.description) + utils.TraverseHelper.update("/Objects/services", + services_node, + environment.description) + + envs.EnvironmentServices.save_environment_description( + session.id, + environment.description, + inner=False + ) + return {"session_id": session.id, "environment_id": environment.id} + def _validate_request(self, request, env_template_id): env_template_exists = env_temps.EnvTemplateServices.env_template_exist if not env_template_exists(env_template_id): diff --git a/murano/tests/unit/api/v1/test_env_templates.py b/murano/tests/unit/api/v1/test_env_templates.py index ed79e480..4efc9be7 100644 --- a/murano/tests/unit/api/v1/test_env_templates.py +++ b/murano/tests/unit/api/v1/test_env_templates.py @@ -27,7 +27,8 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase): def setUp(self): super(TestEnvTemplateApi, self).setUp() self.controller = templates.Controller() - self.uuids = ['env_template_id'] + self.uuids = ['env_template_id', 'other', 'network_id', + 'environment_id', 'session_id'] self.mock_uuid = self._stub_uuid(self.uuids) def test_list_empty_env_templates(self): @@ -237,7 +238,7 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase): "image": "cloud-fedora-v3", "flavor": "m1.medium", "?": { - "type": "io.murano.resources.LinuxMuranoInstance", + "type": "io.murano.resources.Linux", "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" } }, @@ -261,7 +262,7 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase): "image": "cloud-fedora-v3", "flavor": "m1.medium", "?": { - "type": "io.murano.resources.LinuxMuranoInstance", + "type": "io.murano.resources.Linux", "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" } }, @@ -314,7 +315,7 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase): "image": "cloud-fedora-v3", "flavor": "m1.medium", "?": { - "type": "io.murano.resources.LinuxMuranoInstance", + "type": "io.murano.resources.Linux", "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" } }, @@ -418,3 +419,122 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase): '/services/' + service_id) result = req.get_response(self.api) self.assertEqual(404, result.status_code) + + def test_create_environment(self): + """Test that environment is created, session configured.""" + self._set_policy_rules( + {'create_env_template': '@', + 'create_environment': '@'} + ) + + self._create_env_template_no_service() + body_env = {'name': 'my_template'} + + self.expect_policy_check('create_environment', + {'env_template_id': self.uuids[0]}) + req = self._post('/templates/%s/create-environment' % + self.uuids[0], json.dumps(body_env)) + session_result = req.get_response(self.api) + self.assertEqual(200, session_result.status_code) + self.assertIsNotNone(session_result) + body_returned = json.loads(session_result.body) + self.assertEqual(self.uuids[4], body_returned['session_id']) + self.assertEqual(self.uuids[3], body_returned['environment_id']) + + def test_create_env_with_template_no_services(self): + """Test that environment is created and session with template + without services. + """ + self._set_policy_rules( + {'create_env_template': '@', + 'create_environment': '@'} + ) + self._create_env_template_no_service() + + self.expect_policy_check('create_environment', + {'env_template_id': self.uuids[0]}) + body = {'name': 'my_template'} + + req = self._post('/templates/%s/create-environment' % + self.uuids[0], json.dumps(body)) + result = req.get_response(self.api) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + body_returned = json.loads(result.body) + self.assertEqual(self.uuids[4], body_returned['session_id']) + self.assertEqual(self.uuids[3], body_returned['environment_id']) + + def test_mallformed_env_body(self): + """Check that an illegal temp name results in an HTTPClientError.""" + self._set_policy_rules( + {'create_env_template': '@', + 'create_environment': '@'} + ) + self. _create_env_template_no_service() + + self.expect_policy_check('create_environment', + {'env_template_id': self.uuids[0]}) + body = {'invalid': 'test'} + req = self._post('/templates/%s/create-environment' % + self.uuids[0], json.dumps(body)) + result = req.get_response(self.api) + self.assertEqual(400, result.status_code) + + def test_create_env_notexisting_templatebody(self): + """Check that an illegal temp name results in an HTTPClientError.""" + self._set_policy_rules( + {'create_environment': '@'} + ) + env_template_id = 'noexisting' + self.expect_policy_check('create_environment', + {'env_template_id': env_template_id}) + + body = {'name': 'test'} + req = self._post('/templates/%s/create-environment' + % env_template_id, json.dumps(body)) + result = req.get_response(self.api) + self.assertEqual(404, result.status_code) + + def _create_env_template_no_service(self): + self.expect_policy_check('create_env_template') + fake_now = timeutils.utcnow() + timeutils.utcnow.override_time = fake_now + + req = self._post('/templates', json.dumps({'name': 'name'})) + result = req.get_response(self.api) + self.assertEqual(200, result.status_code) + + def _create_env_template_services(self): + fake_now = timeutils.utcnow() + timeutils.utcnow.override_time = fake_now + + self.expect_policy_check('create_env_template') + + fake_now = timeutils.utcnow() + timeutils.utcnow.override_time = fake_now + body = { + "name": "env_template_name", + "services": [ + { + "instance": { + "assignFloatingIp": "true", + "keyname": "mykeyname", + "image": "cloud-fedora-v3", + "flavor": "m1.medium", + "?": { + "type": "io.murano.resources.Linux", + "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" + } + }, + "name": "orion", + "port": "8080", + "?": { + "type": "io.murano.apps.apache.Tomcat", + "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" + } + } + ] + } + + req = self._post('/templates', json.dumps(body)) + req.get_response(self.api)