diff --git a/meta/io.murano/manifest.yaml b/meta/io.murano/manifest.yaml index 34b1e53ae..61ccff3e4 100644 --- a/meta/io.murano/manifest.yaml +++ b/meta/io.murano/manifest.yaml @@ -35,6 +35,8 @@ Classes: io.murano.StackTrace: StackTrace.yaml io.murano.SharedIp: SharedIp.yaml io.murano.File: File.yaml + io.murano.User: User.yaml + io.murano.Project: Project.yaml io.murano.configuration.Linux: configuration/Linux.yaml diff --git a/murano/api/v1/actions.py b/murano/api/v1/actions.py index 4c6e98bac..de1d8e0e5 100644 --- a/murano/api/v1/actions.py +++ b/murano/api/v1/actions.py @@ -57,7 +57,7 @@ class Controller(object): raise exc.HTTPForbidden() task_id = actions.ActionServices.execute( - action_id, session, unit, request.context.auth_token, body or {}) + action_id, session, unit, request.context, body or {}) return {'task_id': task_id} @verify_env diff --git a/murano/api/v1/sessions.py b/murano/api/v1/sessions.py index 172ca80e7..f72d4202e 100644 --- a/murano/api/v1/sessions.py +++ b/murano/api/v1/sessions.py @@ -133,7 +133,7 @@ class Controller(object): envs.EnvironmentServices.deploy(session, unit, - request.context.auth_token) + request.context) def create_resource(): diff --git a/murano/api/v1/static_actions.py b/murano/api/v1/static_actions.py index d78d08537..2d8cc3e42 100644 --- a/murano/api/v1/static_actions.py +++ b/murano/api/v1/static_actions.py @@ -47,7 +47,8 @@ class Controller(object): credentials = { 'token': request.context.auth_token, - 'tenant_id': request.context.tenant + 'project_id': request.context.tenant, + 'user_id': request.context.user } try: diff --git a/murano/common/auth_utils.py b/murano/common/auth_utils.py index 4e1c39e07..ab89ffccb 100644 --- a/murano/common/auth_utils.py +++ b/murano/common/auth_utils.py @@ -176,3 +176,13 @@ def get_session_client_parameters(service_type=None, 'region_name': region }) return result + + +def get_user(uid): + client = _create_keystone_admin_client() + return client.users.get(uid).to_dict() + + +def get_project(pid): + client = _create_keystone_admin_client() + return client.projects.get(pid).to_dict() diff --git a/murano/common/engine.py b/murano/common/engine.py index 9690acae2..9b1b7328d 100644 --- a/murano/common/engine.py +++ b/murano/common/engine.py @@ -182,7 +182,10 @@ class TaskExecutor(object): self._model = task['model'] self._session = execution_session.ExecutionSession() self._session.token = task['token'] - self._session.project_id = task['tenant_id'] + self._session.project_id = task['project_id'] + self._session.user_id = task['user_id'] + self._session.environment_owner_project_id = self._model['project_id'] + self._session.environment_owner_user_id = self._model['user_id'] self._session.system_attributes = self._model.get('SystemData', {}) self._reporter = reporter @@ -202,6 +205,8 @@ class TaskExecutor(object): self._session.system_attributes[ 'Packages'] = pkg_loader.export_fixation_table() self._model['SystemData'] = self._session.system_attributes + self._model['project_id'] = self._session.environment_owner_project_id + self._model['user_id'] = self._session.environment_owner_user_id result['model'] = self._model if (not self._model.get('Objects') and @@ -335,7 +340,8 @@ class StaticActionExecutor(object): self._action = task['action'] self._session = execution_session.ExecutionSession() self._session.token = task['token'] - self._session.project_id = task['tenant_id'] + self._session.project_id = task['project_id'] + self._session.user_id = task['user_id'] self._reporter = reporter self._model_policy_enforcer = enforcer.ModelPolicyEnforcer( self._session) diff --git a/murano/db/services/environments.py b/murano/db/services/environments.py index 87710b8b3..8e7d591e5 100644 --- a/murano/db/services/environments.py +++ b/murano/db/services/environments.py @@ -108,13 +108,15 @@ class EnvironmentServices(object): network_driver) objects['?']['type'] = 'io.murano.Environment' objects['?']['metadata'] = {} - environment_params['tenant_id'] = context.tenant data = { 'Objects': objects, - 'Attributes': [] + 'Attributes': [], + 'project_id': context.tenant, + 'user_id': context.user } + environment_params['tenant_id'] = context.tenant environment = models.Environment() environment.update(environment_params) @@ -235,7 +237,7 @@ class EnvironmentServices(object): } @staticmethod - def deploy(session, unit, token): + def deploy(session, unit, context): environment = unit.query(models.Environment).get( session.environment_id) @@ -243,7 +245,8 @@ class EnvironmentServices(object): 'ObjectsCopy' not in session.description): EnvironmentServices.remove(session.environment_id) else: - sessions.SessionServices.deploy(session, environment, unit, token) + sessions.SessionServices.deploy( + session, environment, unit, context) @staticmethod def _objectify(data, replacements): diff --git a/murano/db/services/sessions.py b/murano/db/services/sessions.py index e37f507cc..3167eaeb0 100644 --- a/murano/db/services/sessions.py +++ b/murano/db/services/sessions.py @@ -102,7 +102,7 @@ class SessionServices(object): return True @staticmethod - def deploy(session, environment, unit, token): + def deploy(session, environment, unit, context): """Prepares and deployes environment Prepares environment for deployment and send deployment command to @@ -118,4 +118,4 @@ class SessionServices(object): actions.ActionServices.submit_task( action_name, environment.id, {}, environment, session, - token, unit) + context, unit) diff --git a/murano/dsl/dsl.py b/murano/dsl/dsl.py index 5fac3e47b..fdf62cbda 100644 --- a/murano/dsl/dsl.py +++ b/murano/dsl/dsl.py @@ -381,3 +381,9 @@ def spawn(func, *args, **kwargs): return func(*args, **kwargs) return eventlet.spawn(wrapper) + + +def new(properties, owner=None, type=None): + context = helpers.get_context() + return helpers.get_object_store().load( + properties, owner, type or get_this(context).type, context=context) diff --git a/murano/engine/execution_session.py b/murano/engine/execution_session.py index ce0e71151..de50ebd4d 100644 --- a/murano/engine/execution_session.py +++ b/murano/engine/execution_session.py @@ -24,6 +24,9 @@ class ExecutionSession(object): def __init__(self): self.token = None self.project_id = None + self.user_id = None + self.environment_owner_project_id = None + self.environment_owner_user_id = None self.trust_id = None self.system_attributes = {} self._set_up_list = [] diff --git a/murano/engine/system/project.py b/murano/engine/system/project.py new file mode 100644 index 000000000..46cd0bef9 --- /dev/null +++ b/murano/engine/system/project.py @@ -0,0 +1,46 @@ +# Copyright (c) 2016 Mirantis Inc. +# +# 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 auth_utils +from murano.dsl import dsl +from murano.dsl import helpers + + +@dsl.name('io.murano.Project') +class Project(object): + @classmethod + def get_current(cls): + fields = auth_utils.get_project( + helpers.get_execution_session().project_id) + return cls._to_object(fields) + + @classmethod + def get_environment_owner(cls): + fields = auth_utils.get_project( + helpers.get_execution_session().environment_owner_project_id) + return cls._to_object(fields) + + @staticmethod + def _to_object(fields): + for field in ('links', 'parent_id', 'enabled'): + fields.pop(field, None) + obj_def = { + 'id': fields.pop('id'), + 'name': fields.pop('name'), + 'domain': fields.pop('domain_id', 'Default'), + 'description': fields.pop('description', None), + 'extra': fields + } + return dsl.new(obj_def) diff --git a/murano/engine/system/system_objects.py b/murano/engine/system/system_objects.py index f14dba125..0370c9e60 100644 --- a/murano/engine/system/system_objects.py +++ b/murano/engine/system/system_objects.py @@ -20,9 +20,11 @@ from murano.engine.system import instance_reporter from murano.engine.system import logger from murano.engine.system import metadef_browser from murano.engine.system import net_explorer +from murano.engine.system import project from murano.engine.system import resource_manager from murano.engine.system import status_reporter from murano.engine.system import test_fixture +from murano.engine.system import user from murano.engine.system import workflowclient @@ -38,3 +40,5 @@ def register(package): package.register_class(test_fixture.TestFixture) package.register_class(workflowclient.MistralClient) package.register_class(metadef_browser.MetadefBrowser) + package.register_class(user.User) + package.register_class(project.Project) diff --git a/murano/engine/system/user.py b/murano/engine/system/user.py new file mode 100644 index 000000000..823bb2db0 --- /dev/null +++ b/murano/engine/system/user.py @@ -0,0 +1,46 @@ +# Copyright (c) 2016 Mirantis Inc. +# +# 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 auth_utils +from murano.dsl import dsl +from murano.dsl import helpers + + +@dsl.name('io.murano.User') +class User(object): + @classmethod + def get_current(cls): + fields = auth_utils.get_user(helpers.get_execution_session().user_id) + return cls._to_object(fields) + + @classmethod + def get_environment_owner(cls): + fields = auth_utils.get_user( + helpers.get_execution_session().environment_owner_user_id) + return cls._to_object(fields) + + @staticmethod + def _to_object(fields): + fields = dict(fields) + for field in ('links', 'enabled', 'default_project_id'): + fields.pop(field, None) + obj_def = { + 'id': fields.pop('id'), + 'name': fields.pop('name'), + 'domain': fields.pop('domain_id', 'Default'), + 'email': fields.pop('email', None), + 'extra': fields + } + return dsl.new(obj_def) diff --git a/murano/services/actions.py b/murano/services/actions.py index 970cdc716..d20d5a272 100644 --- a/murano/services/actions.py +++ b/murano/services/actions.py @@ -19,7 +19,7 @@ from murano.services import states class ActionServices(object): @staticmethod def create_action_task(action_name, target_obj, - args, environment, session, token): + args, environment, session, context): action = None if action_name and target_obj: action = { @@ -30,8 +30,9 @@ class ActionServices(object): task = { 'action': action, 'model': session.description, - 'token': token, - 'tenant_id': environment.tenant_id, + 'token': context.auth_token, + 'project_id': context.tenant, + 'user_id': context.user, 'id': environment.id } if session.description['Objects'] is not None: @@ -58,16 +59,16 @@ class ActionServices(object): @staticmethod def submit_task(action_name, target_obj, - args, environment, session, token, unit): + args, environment, session, context, unit): task = ActionServices.create_action_task( action_name, target_obj, args, - environment, session, token) + environment, session, context) task_id = actions_db.update_task(action_name, session, task, unit) rpc.engine().handle_task(task) return task_id @staticmethod - def execute(action_id, session, unit, token, args=None): + def execute(action_id, session, unit, context, args=None): if args is None: args = {} environment = actions_db.get_environment(session, unit) @@ -79,7 +80,7 @@ class ActionServices(object): return ActionServices.submit_task( action[1]['name'], action[0], args, environment, - session, token, unit) + session, context, unit) @staticmethod def find_action(model, action_id): diff --git a/murano/services/static_actions.py b/murano/services/static_actions.py index ae66dd587..168e346e8 100644 --- a/murano/services/static_actions.py +++ b/murano/services/static_actions.py @@ -30,7 +30,8 @@ class StaticActionServices(object): task = { 'action': action, 'token': credentials['token'], - 'tenant_id': credentials['tenant_id'], + 'project_id': credentials['project_id'], + 'user_id': credentials['user_id'], 'id': str(uuid.uuid4()) } return rpc.engine().call_static_action(task) diff --git a/murano/tests/unit/api/base.py b/murano/tests/unit/api/base.py index 2764cc502..9e58be2cd 100644 --- a/murano/tests/unit/api/base.py +++ b/murano/tests/unit/api/base.py @@ -115,6 +115,7 @@ class ControllerTest(object): # cfg.CONF.set_default('host', 'server.test') self.api_version = '1.0' self.tenant = 'test_tenant' + self.user = 'test_user' self.mock_policy_check = None self.mapper = routes.Mapper() self.api = router.API(self.mapper) diff --git a/murano/tests/unit/api/v1/test_actions.py b/murano/tests/unit/api/v1/test_actions.py index fae827055..a23155729 100644 --- a/murano/tests/unit/api/v1/test_actions.py +++ b/murano/tests/unit/api/v1/test_actions.py @@ -71,7 +71,8 @@ class TestActionsApi(tb.ControllerTest, tb.MuranoApiTestCase): 'method': 'Testaction', 'object_id': '12345' }, - 'tenant_id': self.tenant, + 'project_id': self.tenant, + 'user_id': self.user, 'model': { 'Attributes': {}, 'Objects': { diff --git a/murano/tests/unit/api/v1/test_static_actions.py b/murano/tests/unit/api/v1/test_static_actions.py index 6a63ce7cd..fe286446a 100644 --- a/murano/tests/unit/api/v1/test_static_actions.py +++ b/murano/tests/unit/api/v1/test_static_actions.py @@ -48,7 +48,8 @@ class TestStaticActionsApi(tb.ControllerTest, tb.MuranoApiTestCase): rpc_task = { 'action': action, 'token': None, - 'tenant_id': 'test_tenant', + 'project_id': 'test_tenant', + 'user_id': 'test_user', 'id': mock.ANY } diff --git a/murano/tests/unit/common/test_engine.py b/murano/tests/unit/common/test_engine.py index 564e477e2..3d6f45b17 100644 --- a/murano/tests/unit/common/test_engine.py +++ b/murano/tests/unit/common/test_engine.py @@ -72,15 +72,23 @@ class TestTaskExecutor(base.MuranoTestCase): 'SystemData': { 'Packages': 'my_packages' }, + 'project_id': 'my_tenant_id', + 'user_id': 'my_user_id' }, 'token': 'my_token', - 'tenant_id': 'my_tenant_id', + 'project_id': 'my_tenant_id', + 'user_id': 'my_user_id', 'id': 'my_env_id' } self.task_executor = engine.TaskExecutor(self.task) self.task_executor._model = self.task['model'] self.task_executor._session.token = self.task['token'] - self.task_executor._session.project_id = self.task['tenant_id'] + self.task_executor._session.project_id = self.task['project_id'] + self.task_executor._session.user_id = self.task['user_id'] + self.task_executor._session.environment_owner_project_id_ = \ + self.task['model']['project_id'] + self.task_executor._session.environment_owner_user_id = \ + self.task['model']['user_id'] (self.task_executor._session .system_attributes) = (self.task_executor._model. get('SystemData', {})) @@ -155,7 +163,8 @@ class TestStaticActionExecutor(base.MuranoTestCase): self.task = { 'action': self.action, 'token': 'test_token', - 'tenant_id': 'test_tenant', + 'project_id': 'test_tenant', + 'user_id': 'test_user', 'id': 'test_task_id' } self.task_executor = engine.StaticActionExecutor(self.task) @@ -262,9 +271,14 @@ class TestTaskProcessingEndpoint(base.MuranoTestCase): } self.task = { 'action': self.action, - 'model': {'SystemData': {'TrustId': 'test_trust_id'}}, + 'model': { + 'SystemData': {'TrustId': 'test_trust_id'}, + 'project_id': 'test_tenant', + 'user_id': 'test_user' + }, 'token': 'test_token', - 'tenant_id': 'test_tenant', + 'project_id': 'test_tenant', + 'user_id': 'test_user', 'id': 'test_task_id' } context_manager = mock_context_manager.MockContextManager() @@ -309,7 +323,8 @@ class TestStaticActionEndpoint(base.MuranoTestCase): 'action': self.action, 'model': {'SystemData': {'TrustId': 'test_trust_id'}}, 'token': 'test_token', - 'tenant_id': 'test_tenant', + 'project_id': 'test_tenant', + 'user_id': 'test_user', 'id': 'test_task_id' } context_manager = mock_context_manager.MockContextManager() diff --git a/murano/tests/unit/policy/test_model_policy_enforcer.py b/murano/tests/unit/policy/test_model_policy_enforcer.py index fb22a5fc9..5da80f3cb 100644 --- a/murano/tests/unit/policy/test_model_policy_enforcer.py +++ b/murano/tests/unit/policy/test_model_policy_enforcer.py @@ -36,9 +36,13 @@ class TestModelPolicyEnforcer(base.MuranoTestCase): self.task = { 'action': {'method': 'deploy'}, - 'model': {'Objects': None}, + 'model': {'Objects': None, + 'project_id': 'tenant', + 'user_id': 'user' + }, 'token': 'token', - 'tenant_id': 'environment.tenant_id', + 'project_id': 'tenant', + 'user_id': 'user', 'id': 'environment.id' } diff --git a/murano/tests/unit/services/test_actions.py b/murano/tests/unit/services/test_actions.py index 685d06fa4..9675a8f37 100644 --- a/murano/tests/unit/services/test_actions.py +++ b/murano/tests/unit/services/test_actions.py @@ -34,11 +34,14 @@ class TestActions(test_base.MuranoTestCase): }, 'applications': [], 'services': ['service1', 'service2'] - } + }, + 'project_id': 'XXX', + 'user_id': 'YYY' } mock_session = mock.MagicMock(description=mock_description) - mock_token = 'test_token' - + mock_context = mock.Mock(auth_token='test_token', + tenant='test_tenant', + user='test_user') expected_task = { 'action': { 'object_id': mock_target_obj, @@ -52,10 +55,13 @@ class TestActions(test_base.MuranoTestCase): }, 'applications': mock_session.description['Objects']['services'] - } + }, + 'project_id': 'XXX', + 'user_id': 'YYY' }, - 'token': mock_token, - 'tenant_id': mock_environment.tenant_id, + 'token': 'test_token', + 'project_id': 'test_tenant', + 'user_id': 'test_user', 'id': mock_environment.id } @@ -64,7 +70,7 @@ class TestActions(test_base.MuranoTestCase): mock_args, mock_environment, mock_session, - mock_token) + mock_context) self.assertEqual(expected_task, task) @@ -115,7 +121,10 @@ class TestActions(test_base.MuranoTestCase): test_args = 'test_args' test_environment = 'test_environment' test_session = 'test_session' - test_token = 'test_token' + context = mock.Mock() + context.auth_token = 'test_token' + context.tenant = 'test_tenant' + context.user = 'test_user' test_unit = 'test_unit' task_id = actions.ActionServices.submit_task(test_action_name, @@ -123,7 +132,7 @@ class TestActions(test_base.MuranoTestCase): test_args, test_environment, test_session, - test_token, + context, test_unit) self.assertEqual('123', task_id) @@ -132,7 +141,7 @@ class TestActions(test_base.MuranoTestCase): test_args, test_environment, test_session, - test_token) + context) mock_update_task.assert_called_once_with(test_action_name, test_session, mock_task, test_unit) diff --git a/releasenotes/notes/user-project-6173d7282765b5ca.yaml b/releasenotes/notes/user-project-6173d7282765b5ca.yaml new file mode 100644 index 000000000..d9ecedb19 --- /dev/null +++ b/releasenotes/notes/user-project-6173d7282765b5ca.yaml @@ -0,0 +1,5 @@ +--- +features: + - Added classes that represent OpenStack user and project + - Added ability to retrieve current user and project info + - Added ability to retrieve environment owner user and project info