Ability to retrieve current/owner user/project

Added an ability to retrieve information about the current user,
current project, environment owner (both user and project)
from keystone. Appropriate information (including
extra fields but excluding internal system data) is fetched from
Keystone using the same service credentials that are used to validate
tokens, create trusts etc.

- io.murano.User and io.murano.Project classes were added.
- Both classes have 2 static methods to get current and environment
   owner object of appropriate class
- Object model now contains project_id/user_id of the user who
   created the environment
- Deployment task contains project_id (renamed from tenant_id)
   and user_id of the user who initiated the deployment

Change-Id: Ic7e24c1d2b669ed315851047bcdb27e075cfc56b
This commit is contained in:
Alexander Tivelkov 2016-11-23 18:46:02 +03:00 committed by Stan Lagun
parent 3a18488cf4
commit 81eebd12ad
22 changed files with 204 additions and 39 deletions

View File

@ -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

View File

@ -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

View File

@ -133,7 +133,7 @@ class Controller(object):
envs.EnvironmentServices.deploy(session,
unit,
request.context.auth_token)
request.context)
def create_resource():

View File

@ -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:

View File

@ -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()

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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 = []

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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': {

View File

@ -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
}

View File

@ -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()

View File

@ -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'
}

View File

@ -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)

View File

@ -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