Support multi-project in xjob
1. What is the problem We utilize an admin user to operate resources in asynchronous job. Currently the project of this user is pre-configured, so when this user try to operate resources which do not belong to the configured project, operation will fail. 2. What is the solution for the problem During deployment, we create an admin user and assign admin role to it in each projects that need to be operated. And in the codes, since now we have project_id in job table, we can use this project_id to retrieve an admin token, instead of using the pre-configured project_id. 3. What features need to be implemented to the Tricircle to realize the solution Support using different project_id according to the operated resources to get admin token when running asynchronous job. Change-Id: Id3eb464bc0573f96cecfa4700cc977e328054d10
This commit is contained in:
parent
92c3256b0a
commit
ad5e6940e3
|
@ -20,8 +20,8 @@ import six
|
|||
from six.moves import xrange
|
||||
import uuid
|
||||
|
||||
from keystoneclient.auth import token_endpoint
|
||||
from keystoneclient import session
|
||||
import keystoneauth1.identity.generic as auth_identity
|
||||
from keystoneauth1 import session
|
||||
from keystoneclient.v3 import client as keystone_client
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
@ -92,6 +92,7 @@ def _safe_operation(operation_name):
|
|||
try:
|
||||
service = instance.resource_service_map[resource]
|
||||
instance._ensure_endpoint_set(context, service)
|
||||
instance._ensure_token_for_admin(context)
|
||||
return func(*args, **kwargs)
|
||||
except exceptions.EndpointNotAvailable as e:
|
||||
if i == retries:
|
||||
|
@ -211,23 +212,23 @@ class Client(object):
|
|||
resource))
|
||||
|
||||
@staticmethod
|
||||
def _get_keystone_session():
|
||||
return resource_handle.ResourceHandle.get_keystone_session()
|
||||
def _get_keystone_session(project_id=None):
|
||||
return resource_handle.ResourceHandle.get_keystone_session(project_id)
|
||||
|
||||
@staticmethod
|
||||
def get_admin_token():
|
||||
return Client._get_admin_token()
|
||||
def get_admin_token(project_id=None):
|
||||
return Client._get_admin_token(project_id)
|
||||
|
||||
@staticmethod
|
||||
def _get_admin_token():
|
||||
return Client._get_keystone_session().get_token()
|
||||
def _get_admin_token(project_id=None):
|
||||
return Client._get_keystone_session(project_id).get_token()
|
||||
|
||||
def _get_admin_project_id(self):
|
||||
return self._get_keystone_session().get_project_id()
|
||||
|
||||
def _get_endpoint_from_keystone(self, cxt):
|
||||
auth = token_endpoint.Token(cfg.CONF.client.identity_url,
|
||||
cxt.auth_token)
|
||||
auth = auth_identity.Token(cfg.CONF.client.auth_url,
|
||||
cxt.auth_token, tenant_id=cxt.tenant)
|
||||
sess = session.Session(auth=auth)
|
||||
cli = keystone_client.Client(session=sess)
|
||||
|
||||
|
@ -292,8 +293,8 @@ class Client(object):
|
|||
:return: None
|
||||
"""
|
||||
if is_internal:
|
||||
admin_context = tricircle_context.Context()
|
||||
admin_context.auth_token = self._get_admin_token()
|
||||
admin_context = tricircle_context.get_admin_context()
|
||||
self._ensure_token_for_admin(admin_context)
|
||||
endpoint_map = self._get_endpoint_from_keystone(admin_context)
|
||||
else:
|
||||
endpoint_map = self._get_endpoint_from_keystone(cxt)
|
||||
|
@ -362,9 +363,17 @@ class Client(object):
|
|||
|
||||
def get_keystone_client_by_context(self, ctx):
|
||||
client_session = self._get_keystone_session()
|
||||
return keystone_client.Client(auth_url=cfg.CONF.client.identity_url,
|
||||
return keystone_client.Client(auth_url=cfg.CONF.client.auth_url,
|
||||
session=client_session)
|
||||
|
||||
def _ensure_token_for_admin(self, cxt):
|
||||
if cxt.is_admin and not cxt.auth_token:
|
||||
if cxt.tenant:
|
||||
cxt.auth_token = self._get_admin_token(cxt.tenant)
|
||||
else:
|
||||
cxt.auth_token = self._get_admin_token()
|
||||
cxt.tenant = self._get_admin_project_id()
|
||||
|
||||
@_safe_operation('client')
|
||||
def get_native_client(self, resource, cxt):
|
||||
"""Get native python client instance
|
||||
|
@ -375,10 +384,6 @@ class Client(object):
|
|||
:param cxt: resource type
|
||||
:return: client instance
|
||||
"""
|
||||
if cxt.is_admin and not cxt.auth_token:
|
||||
cxt.auth_token = self._get_admin_token()
|
||||
cxt.tenant = self._get_admin_project_id()
|
||||
|
||||
service = self.resource_service_map[resource]
|
||||
handle = self.service_handle_map[service]
|
||||
return handle._get_client(cxt)
|
||||
|
@ -401,10 +406,6 @@ class Client(object):
|
|||
:return: list of dict containing resources information
|
||||
:raises: EndpointNotAvailable
|
||||
"""
|
||||
if cxt.is_admin and not cxt.auth_token:
|
||||
cxt.auth_token = self._get_admin_token()
|
||||
cxt.tenant = self._get_admin_project_id()
|
||||
|
||||
service = self.resource_service_map[resource]
|
||||
handle = self.service_handle_map[service]
|
||||
filters = filters or []
|
||||
|
@ -435,10 +436,6 @@ class Client(object):
|
|||
:return: a dict containing resource information
|
||||
:raises: EndpointNotAvailable
|
||||
"""
|
||||
if cxt.is_admin and not cxt.auth_token:
|
||||
cxt.auth_token = self._get_admin_token()
|
||||
cxt.tenant = self._get_admin_project_id()
|
||||
|
||||
service = self.resource_service_map[resource]
|
||||
handle = self.service_handle_map[service]
|
||||
return handle.handle_create(cxt, resource, *args, **kwargs)
|
||||
|
@ -464,10 +461,6 @@ class Client(object):
|
|||
:return: a dict containing resource information
|
||||
:raises: EndpointNotAvailable
|
||||
"""
|
||||
if cxt.is_admin and not cxt.auth_token:
|
||||
cxt.auth_token = self._get_admin_token()
|
||||
cxt.tenant = self._get_admin_project_id()
|
||||
|
||||
service = self.resource_service_map[resource]
|
||||
handle = self.service_handle_map[service]
|
||||
return handle.handle_update(cxt, resource, *args, **kwargs)
|
||||
|
@ -486,10 +479,6 @@ class Client(object):
|
|||
:return: None
|
||||
:raises: EndpointNotAvailable
|
||||
"""
|
||||
if cxt.is_admin and not cxt.auth_token:
|
||||
cxt.auth_token = self._get_admin_token()
|
||||
cxt.tenant = self._get_admin_project_id()
|
||||
|
||||
service = self.resource_service_map[resource]
|
||||
handle = self.service_handle_map[service]
|
||||
return handle.handle_delete(cxt, resource, resource_id)
|
||||
|
@ -508,10 +497,6 @@ class Client(object):
|
|||
:return: a dict containing resource information
|
||||
:raises: EndpointNotAvailable
|
||||
"""
|
||||
if cxt.is_admin and not cxt.auth_token:
|
||||
cxt.auth_token = self._get_admin_token()
|
||||
cxt.tenant = self._get_admin_project_id()
|
||||
|
||||
service = self.resource_service_map[resource]
|
||||
handle = self.service_handle_map[service]
|
||||
return handle.handle_get(cxt, resource, resource_id)
|
||||
|
@ -546,10 +531,6 @@ class Client(object):
|
|||
:return: None
|
||||
:raises: EndpointNotAvailable
|
||||
"""
|
||||
if cxt.is_admin and not cxt.auth_token:
|
||||
cxt.auth_token = self._get_admin_token()
|
||||
cxt.tenant = self._get_admin_project_id()
|
||||
|
||||
service = self.resource_service_map[resource]
|
||||
handle = self.service_handle_map[service]
|
||||
return handle.handle_action(cxt, resource, action, *args, **kwargs)
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystoneclient.auth.identity import v3 as auth_identity
|
||||
from keystoneclient import session
|
||||
import keystoneauth1.identity.generic as auth_identity
|
||||
from keystoneauth1 import session
|
||||
|
||||
from neutronclient.common import exceptions as q_exceptions
|
||||
from neutronclient.neutron import client as q_client
|
||||
|
@ -62,19 +62,23 @@ class ResourceHandle(object):
|
|||
self.endpoint_url = url
|
||||
|
||||
@staticmethod
|
||||
def get_keystone_session():
|
||||
auth = auth_identity.Password(
|
||||
auth_url=cfg.CONF.client.identity_url,
|
||||
username=cfg.CONF.client.admin_username,
|
||||
password=cfg.CONF.client.admin_password,
|
||||
project_name=cfg.CONF.client.admin_tenant,
|
||||
user_domain_name=cfg.CONF.client.admin_user_domain_name,
|
||||
project_domain_name=cfg.CONF.client.admin_tenant_domain_name)
|
||||
def get_keystone_session(project_id=None):
|
||||
kwargs = {
|
||||
'auth_url': cfg.CONF.client.auth_url,
|
||||
'username': cfg.CONF.client.admin_username,
|
||||
'password': cfg.CONF.client.admin_password,
|
||||
'user_domain_name': cfg.CONF.client.admin_user_domain_name,
|
||||
'project_domain_name': cfg.CONF.client.admin_tenant_domain_name}
|
||||
if not project_id:
|
||||
kwargs['project_name'] = cfg.CONF.client.admin_tenant
|
||||
else:
|
||||
kwargs['project_id'] = project_id
|
||||
auth = auth_identity.Password(**kwargs)
|
||||
return session.Session(auth=auth)
|
||||
|
||||
@staticmethod
|
||||
def get_admin_token():
|
||||
return ResourceHandle.get_keystone_session().get_token()
|
||||
def get_admin_token(project_id=None):
|
||||
return ResourceHandle.get_keystone_session(project_id).get_token()
|
||||
|
||||
|
||||
class NeutronResourceHandle(ResourceHandle):
|
||||
|
@ -91,7 +95,7 @@ class NeutronResourceHandle(ResourceHandle):
|
|||
def _get_client(self, cxt):
|
||||
token = cxt.auth_token
|
||||
if not token and cxt.is_admin:
|
||||
token = self.get_admin_token()
|
||||
token = self.get_admin_token(cxt.tenant)
|
||||
return q_client.Client('2.0',
|
||||
token=token,
|
||||
auth_url=self.auth_url,
|
||||
|
|
|
@ -269,7 +269,7 @@ class TricirclePlugin(plugin.Ml2Plugin):
|
|||
t_ctx = t_context.get_context_from_neutron_context(context)
|
||||
if self._skip_non_api_query(t_ctx):
|
||||
return b_networks
|
||||
t_ctx.auth_token = client.Client.get_admin_token()
|
||||
t_ctx.auth_token = client.Client.get_admin_token(context.project_id)
|
||||
raw_client = self.neutron_handle._get_client(t_ctx)
|
||||
params = self._construct_params(filters, sorts, limit, marker,
|
||||
page_reverse)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
DEST=$BASE/new
|
||||
DEVSTACK_DIR=$DEST/devstack
|
||||
source $DEVSTACK_DIR/openrc admin demo
|
||||
source $DEVSTACK_DIR/openrc admin admin
|
||||
unset OS_REGION_NAME
|
||||
|
||||
openstacktop="openstack --os-region-name CentralRegion"
|
||||
|
@ -92,7 +92,7 @@ if [ "$DATABASE_TYPE" == "mysql" ]; then
|
|||
full_count=$(echo $full_result | grep -o "[0-9]\{1,\}")
|
||||
if [ $full_count -ne 0 ]; then
|
||||
echo "Wait for job to finish"
|
||||
sleep 5
|
||||
sleep 10
|
||||
else
|
||||
break
|
||||
fi
|
||||
|
@ -114,7 +114,7 @@ else
|
|||
full_count=$(echo $full_result | grep -o "[0-9]\{1,\}")
|
||||
if [ $full_count -ne 0 ]; then
|
||||
echo "Wait for job to finish"
|
||||
sleep 5
|
||||
sleep 10
|
||||
else
|
||||
break
|
||||
fi
|
||||
|
|
|
@ -255,6 +255,7 @@ class ClientTest(unittest.TestCase):
|
|||
api.delete_cached_endpoints(self.context, FAKE_SERVICE_ID)
|
||||
|
||||
self.client._get_admin_token = mock.Mock()
|
||||
self.client._get_admin_project_id = mock.Mock()
|
||||
self.client._get_endpoint_from_keystone = mock.Mock()
|
||||
|
||||
self.client._get_endpoint_from_keystone.return_value = {}
|
||||
|
@ -294,6 +295,7 @@ class ClientTest(unittest.TestCase):
|
|||
update_dict)
|
||||
|
||||
self.client._get_admin_token = mock.Mock()
|
||||
self.client._get_admin_project_id = mock.Mock()
|
||||
self.client._get_endpoint_from_keystone = mock.Mock()
|
||||
self.client._get_endpoint_from_keystone.return_value = {}
|
||||
# retry but still endpoint not updated
|
||||
|
|
|
@ -159,6 +159,7 @@ class FakeContext(object):
|
|||
def __init__(self):
|
||||
self.session = FakeSession()
|
||||
self.auth_token = 'token'
|
||||
self.project_id = ''
|
||||
|
||||
|
||||
class FakeClient(object):
|
||||
|
|
|
@ -259,6 +259,8 @@ class XManager(PeriodicTasks):
|
|||
'%(job_type)s',
|
||||
{'status': 'new' if is_new_job else 'failed',
|
||||
'resource_id': resource_id, 'job_type': job_type})
|
||||
# this is an admin context, we set the correct project id
|
||||
ctx.tenant = project_id
|
||||
if not is_new_job:
|
||||
db_api.new_job(ctx, project_id, job_type, resource_id)
|
||||
self.job_handles[job_type](ctx, payload=payload)
|
||||
|
|
Loading…
Reference in New Issue