4f2b97c30e
Since we look up the instance and call to its cell in consoleauth, we need to look up the InstanceMapping and properly target the database and rpc operation. Related to blueprint cells-aware-api Change-Id: I80013fa59b221f70376d6e1d4080ca699ff6caeb
146 lines
5.2 KiB
Python
146 lines
5.2 KiB
Python
#!/usr/bin/env python
|
|
# Copyright (c) 2012 OpenStack Foundation
|
|
# All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
"""Auth Components for Consoles."""
|
|
|
|
import time
|
|
|
|
from oslo_log import log as logging
|
|
import oslo_messaging as messaging
|
|
from oslo_serialization import jsonutils
|
|
|
|
from nova import cache_utils
|
|
from nova.cells import rpcapi as cells_rpcapi
|
|
from nova.compute import rpcapi as compute_rpcapi
|
|
import nova.conf
|
|
from nova import context as nova_context
|
|
from nova.i18n import _LI
|
|
from nova import manager
|
|
from nova import objects
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
CONF = nova.conf.CONF
|
|
|
|
|
|
class ConsoleAuthManager(manager.Manager):
|
|
"""Manages token based authentication."""
|
|
|
|
target = messaging.Target(version='2.1')
|
|
|
|
def __init__(self, scheduler_driver=None, *args, **kwargs):
|
|
super(ConsoleAuthManager, self).__init__(service_name='consoleauth',
|
|
*args, **kwargs)
|
|
self._mc = None
|
|
self._mc_instance = None
|
|
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
|
|
self.cells_rpcapi = cells_rpcapi.CellsAPI()
|
|
|
|
@property
|
|
def mc(self):
|
|
if self._mc is None:
|
|
self._mc = cache_utils.get_client(CONF.consoleauth.token_ttl)
|
|
return self._mc
|
|
|
|
@property
|
|
def mc_instance(self):
|
|
if self._mc_instance is None:
|
|
self._mc_instance = cache_utils.get_client()
|
|
return self._mc_instance
|
|
|
|
def reset(self):
|
|
LOG.info(_LI('Reloading compute RPC API'))
|
|
compute_rpcapi.LAST_VERSION = None
|
|
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
|
|
|
|
def _get_tokens_for_instance(self, instance_uuid):
|
|
tokens_str = self.mc_instance.get(instance_uuid.encode('UTF-8'))
|
|
if not tokens_str:
|
|
tokens = []
|
|
else:
|
|
tokens = jsonutils.loads(tokens_str)
|
|
return tokens
|
|
|
|
def authorize_console(self, context, token, console_type, host, port,
|
|
internal_access_path, instance_uuid,
|
|
access_url=None):
|
|
|
|
token_dict = {'token': token,
|
|
'instance_uuid': instance_uuid,
|
|
'console_type': console_type,
|
|
'host': host,
|
|
'port': port,
|
|
'internal_access_path': internal_access_path,
|
|
'access_url': access_url,
|
|
'last_activity_at': time.time()}
|
|
data = jsonutils.dumps(token_dict)
|
|
|
|
self.mc.set(token.encode('UTF-8'), data)
|
|
tokens = self._get_tokens_for_instance(instance_uuid)
|
|
|
|
# Remove the expired tokens from cache.
|
|
token_values = self.mc.get_multi(
|
|
[tok.encode('UTF-8') for tok in tokens])
|
|
tokens = [name for name, value in zip(tokens, token_values)
|
|
if value is not None]
|
|
tokens.append(token)
|
|
|
|
self.mc_instance.set(instance_uuid.encode('UTF-8'),
|
|
jsonutils.dumps(tokens))
|
|
|
|
LOG.info(_LI("Received Token: %(token)s, %(token_dict)s"),
|
|
{'token': token, 'token_dict': token_dict})
|
|
|
|
def _validate_token(self, context, token):
|
|
instance_uuid = token['instance_uuid']
|
|
if instance_uuid is None:
|
|
return False
|
|
|
|
# NOTE(comstud): consoleauth was meant to run in API cells. So,
|
|
# if cells is enabled, we must call down to the child cell for
|
|
# the instance.
|
|
if CONF.cells.enable:
|
|
return self.cells_rpcapi.validate_console_port(context,
|
|
instance_uuid, token['port'], token['console_type'])
|
|
|
|
mapping = objects.InstanceMapping.get_by_instance_uuid(context,
|
|
instance_uuid)
|
|
with nova_context.target_cell(context, mapping.cell_mapping):
|
|
instance = objects.Instance.get_by_uuid(context, instance_uuid)
|
|
|
|
return self.compute_rpcapi.validate_console_port(
|
|
context,
|
|
instance,
|
|
token['port'],
|
|
token['console_type'])
|
|
|
|
def check_token(self, context, token):
|
|
token_str = self.mc.get(token.encode('UTF-8'))
|
|
token_valid = (token_str is not None)
|
|
LOG.info(_LI("Checking Token: %(token)s, %(token_valid)s"),
|
|
{'token': token, 'token_valid': token_valid})
|
|
if token_valid:
|
|
token = jsonutils.loads(token_str)
|
|
if self._validate_token(context, token):
|
|
return token
|
|
|
|
def delete_tokens_for_instance(self, context, instance_uuid):
|
|
tokens = self._get_tokens_for_instance(instance_uuid)
|
|
self.mc.delete_multi(
|
|
[tok.encode('UTF-8') for tok in tokens])
|
|
self.mc_instance.delete(instance_uuid.encode('UTF-8'))
|