diff options
25 files changed, 400 insertions, 196 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index b3f9bd099..dcbde3428 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -42,6 +42,7 @@ from nova import db from nova import exception from nova.image import s3 from nova import network +from nova.network.security_group import quantum_driver from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import quota @@ -1696,6 +1697,15 @@ class CloudSecurityGroupNovaAPI(compute_api.SecurityGroupAPI, pass +class CloudSecurityGroupQuantumAPI(quantum_driver.SecurityGroupAPI, + EC2SecurityGroupExceptions): + pass + + def get_cloud_security_group_api(): if cfg.CONF.security_group_api.lower() == 'nova': return CloudSecurityGroupNovaAPI() + elif cfg.CONF.security_group_api.lower() == 'quantum': + return CloudSecurityGroupQuantumAPI() + else: + raise NotImplementedError() diff --git a/nova/common/memorycache.py b/nova/common/memorycache.py index f89e4b265..c124784d5 100644 --- a/nova/common/memorycache.py +++ b/nova/common/memorycache.py @@ -83,3 +83,8 @@ class Client(object): new_value = int(value) + delta self.cache[key] = (self.cache[key][0], str(new_value)) return new_value + + def delete(self, key, time=0): + """Deletes the value associated with a key.""" + if key in self.cache: + del self.cache[key] diff --git a/nova/compute/api.py b/nova/compute/api.py index cc07a998a..f917e379d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -2189,8 +2189,9 @@ class API(base.Base): instance=instance, console_type=console_type) self.consoleauth_rpcapi.authorize_console(context, - connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['token'], console_type, + connect_info['host'], connect_info['port'], + connect_info['internal_access_path'], instance['uuid']) return {'url': connect_info['access_url']} @@ -2207,10 +2208,11 @@ class API(base.Base): """Get a url to an instance Console.""" connect_info = self.compute_rpcapi.get_spice_console(context, instance=instance, console_type=console_type) - + print connect_info self.consoleauth_rpcapi.authorize_console(context, - connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['token'], console_type, + connect_info['host'], connect_info['port'], + connect_info['internal_access_path'], instance['uuid']) return {'url': connect_info['access_url']} diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py index 1e30331bc..22e31a8e1 100644 --- a/nova/compute/cells_api.py +++ b/nova/compute/cells_api.py @@ -465,7 +465,8 @@ class ComputeCellsAPI(compute_api.API): self.consoleauth_rpcapi.authorize_console(context, connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['port'], connect_info['internal_access_path'], + instance_uuid=instance['uuid']) return {'url': connect_info['access_url']} @wrap_check_policy @@ -480,7 +481,8 @@ class ComputeCellsAPI(compute_api.API): self.consoleauth_rpcapi.authorize_console(context, connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['port'], connect_info['internal_access_path'], + instance_uuid=instance['uuid']) return {'url': connect_info['access_url']} @validate_cell diff --git a/nova/compute/manager.py b/nova/compute/manager.py index afeb9f02e..99b97e921 100755 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -50,6 +50,7 @@ from nova.compute import task_states from nova.compute import utils as compute_utils from nova.compute import vm_states from nova import conductor +from nova import consoleauth import nova.context from nova import exception from nova import hooks @@ -317,7 +318,7 @@ class ComputeVirtAPI(virtapi.VirtAPI): class ComputeManager(manager.SchedulerDependentManager): """Manages the running instances from creation to destruction.""" - RPC_API_VERSION = '2.25' + RPC_API_VERSION = '2.26' def __init__(self, compute_driver=None, *args, **kwargs): """Load configuration options and connect to the hypervisor.""" @@ -335,6 +336,8 @@ class ComputeManager(manager.SchedulerDependentManager): self.conductor_api = conductor.API() self.is_quantum_security_groups = ( openstack_driver.is_quantum_security_groups()) + self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI() + super(ComputeManager, self).__init__(service_name="compute", *args, **kwargs) @@ -1223,6 +1226,10 @@ class ComputeManager(manager.SchedulerDependentManager): self._notify_about_instance_usage(context, instance, "delete.end", system_metadata=system_meta) + if CONF.vnc_enabled or CONF.spice.enabled: + self.consoleauth_rpcapi.delete_tokens_for_instance(context, + instance['uuid']) + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @wrap_instance_event @wrap_instance_fault @@ -2394,9 +2401,9 @@ class ComputeManager(manager.SchedulerDependentManager): return self.driver.set_host_enabled(host, enabled) @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) - def get_host_uptime(self, context, host): + def get_host_uptime(self, context): """Returns the result of calling "uptime" on the target host.""" - return self.driver.get_host_uptime(host) + return self.driver.get_host_uptime(self.host) @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @wrap_instance_fault @@ -2555,6 +2562,16 @@ class ComputeManager(manager.SchedulerDependentManager): return connect_info + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) + @wrap_instance_fault + def validate_console_port(self, ctxt, instance, port, console_type): + if console_type == "spice-html5": + console_info = self.driver.get_spice_console(instance) + else: + console_info = self.driver.get_vnc_console(instance) + + return console_info['port'] == port + def _attach_volume_boot(self, context, instance, volume, mountpoint): """Attach a volume to an instance at boot time. So actual attach is done by instance creation""" diff --git a/nova/compute/rpcapi.py b/nova/compute/rpcapi.py index 0be9972da..67dfc6a6b 100644 --- a/nova/compute/rpcapi.py +++ b/nova/compute/rpcapi.py @@ -161,6 +161,8 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): 2.23 - Remove network_info from reboot_instance 2.24 - Added get_spice_console method 2.25 - Add attach_interface() and detach_interface() + 2.26 - Add validate_console_token to ensure the service connects to + vnc on the correct port ''' # @@ -321,6 +323,14 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): topic=_compute_topic(self.topic, ctxt, None, instance), version='2.24') + def validate_console_port(self, ctxt, instance, port, console_type): + instance_p = jsonutils.to_primitive(instance) + return self.call(ctxt, self.make_msg('validate_console_port', + instance=instance_p, port=port, console_type=console_type), + topic=_compute_topic(self.topic, ctxt, + None, instance), + version='2.26') + def host_maintenance_mode(self, ctxt, host_param, mode, host): '''Set host maintenance mode diff --git a/nova/consoleauth/manager.py b/nova/consoleauth/manager.py index 74321a27b..56e94dffd 100644 --- a/nova/consoleauth/manager.py +++ b/nova/consoleauth/manager.py @@ -23,6 +23,8 @@ import time from oslo.config import cfg from nova.common import memorycache +from nova.compute import rpcapi as compute_rpcapi +from nova.conductor import api as conductor_api from nova import manager from nova.openstack.common import jsonutils from nova.openstack.common import log as logging @@ -46,15 +48,27 @@ CONF.register_opts(consoleauth_opts) class ConsoleAuthManager(manager.Manager): """Manages token based authentication.""" - RPC_API_VERSION = '1.1' + RPC_API_VERSION = '1.2' def __init__(self, scheduler_driver=None, *args, **kwargs): super(ConsoleAuthManager, self).__init__(*args, **kwargs) self.mc = memorycache.get_client() + self.compute_rpcapi = compute_rpcapi.ComputeAPI() + self.conductor_api = conductor_api.API() + + def _get_tokens_for_instance(self, instance_uuid): + tokens_str = self.mc.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): + internal_access_path, instance_uuid=None): + token_dict = {'token': token, + 'instance_uuid': instance_uuid, 'console_type': console_type, 'host': host, 'port': port, @@ -62,14 +76,39 @@ class ConsoleAuthManager(manager.Manager): 'last_activity_at': time.time()} data = jsonutils.dumps(token_dict) self.mc.set(token.encode('UTF-8'), data, CONF.console_token_ttl) + if instance_uuid is not None: + tokens = self._get_tokens_for_instance(instance_uuid) + tokens.append(token) + self.mc.set(instance_uuid.encode('UTF-8'), + jsonutils.dumps(tokens)) + LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals()) + def _validate_token(self, context, token): + instance_uuid = token['instance_uuid'] + if instance_uuid is None: + return False + instance = self.conductor_api.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.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals()) if token_valid: - return jsonutils.loads(token_str) + 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) + for token in tokens: + self.mc.delete(token) + self.mc.delete(instance_uuid.encode('UTF-8')) def get_backdoor_port(self, context): return self.backdoor_port diff --git a/nova/consoleauth/rpcapi.py b/nova/consoleauth/rpcapi.py index 813143f76..474f3ad19 100644 --- a/nova/consoleauth/rpcapi.py +++ b/nova/consoleauth/rpcapi.py @@ -32,6 +32,8 @@ class ConsoleAuthAPI(nova.openstack.common.rpc.proxy.RpcProxy): 1.0 - Initial version. 1.1 - Added get_backdoor_port() + 1.2 - Added instance_uuid to authorize_console, and + delete_tokens_for_instance ''' # @@ -50,18 +52,26 @@ class ConsoleAuthAPI(nova.openstack.common.rpc.proxy.RpcProxy): default_version=self.BASE_RPC_API_VERSION) def authorize_console(self, ctxt, token, console_type, host, port, - internal_access_path): + internal_access_path, instance_uuid=None): # The remote side doesn't return anything, but we want to block # until it completes. return self.call(ctxt, self.make_msg('authorize_console', token=token, console_type=console_type, host=host, port=port, - internal_access_path=internal_access_path)) + internal_access_path=internal_access_path, + instance_uuid=instance_uuid), + version="1.2") def check_token(self, ctxt, token): return self.call(ctxt, self.make_msg('check_token', token=token)) + def delete_tokens_for_instance(self, ctxt, instance_uuid): + return self.call(ctxt, + self.make_msg('delete_tokens_for_instance', + instance_uuid=instance_uuid), + version="1.2") + def get_backdoor_port(self, ctxt, host): return self.call(ctxt, self.make_msg('get_backdoor_port'), version='1.1') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 360bd1b3a..1baa192a3 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2818,7 +2818,7 @@ def volume_get_iscsi_target_num(context, volume_id): @require_context def ec2_volume_create(context, volume_uuid, id=None): - """Create ec2 compatable volume by provided uuid.""" + """Create ec2 compatible volume by provided uuid.""" ec2_volume_ref = models.VolumeIdMapping() ec2_volume_ref.update({'uuid': volume_uuid}) if id is not None: @@ -2855,7 +2855,7 @@ def get_volume_uuid_by_ec2_id(context, ec2_id, session=None): @require_context def ec2_snapshot_create(context, snapshot_uuid, id=None): - """Create ec2 compatable snapshot by provided uuid.""" + """Create ec2 compatible snapshot by provided uuid.""" ec2_snapshot_ref = models.SnapshotIdMapping() ec2_snapshot_ref.update({'uuid': snapshot_uuid}) if id is not None: @@ -4640,7 +4640,7 @@ def action_event_get_by_id(context, action_id, event_id): @require_context def ec2_instance_create(context, instance_uuid, id=None): - """Create ec2 compatable instance by provided uuid.""" + """Create ec2 compatible instance by provided uuid.""" ec2_instance_ref = models.InstanceIdMapping() ec2_instance_ref.update({'uuid': instance_uuid}) if id is not None: diff --git a/nova/openstack/common/eventlet_backdoor.py b/nova/openstack/common/eventlet_backdoor.py index 61ceded43..8b81ebf8e 100644 --- a/nova/openstack/common/eventlet_backdoor.py +++ b/nova/openstack/common/eventlet_backdoor.py @@ -51,12 +51,20 @@ def _print_greenthreads(): print +def _print_nativethreads(): + for threadId, stack in sys._current_frames().items(): + print threadId + traceback.print_stack(stack) + print + + def initialize_if_enabled(): backdoor_locals = { 'exit': _dont_use_this, # So we don't exit the entire process 'quit': _dont_use_this, # So we don't exit the entire process 'fo': _find_objects, 'pgt': _print_greenthreads, + 'pnt': _print_nativethreads, } if CONF.backdoor_port is None: diff --git a/nova/openstack/common/rpc/__init__.py b/nova/openstack/common/rpc/__init__.py index ff72c3f8e..991820b7c 100644 --- a/nova/openstack/common/rpc/__init__.py +++ b/nova/openstack/common/rpc/__init__.py @@ -34,6 +34,7 @@ from nova.openstack.common.gettextutils import _ from nova.openstack.common import importutils from nova.openstack.common import local + LOG = logging.getLogger(__name__) diff --git a/nova/openstack/common/rpc/impl_kombu.py b/nova/openstack/common/rpc/impl_kombu.py index 5e1846f91..180685cf3 100644 --- a/nova/openstack/common/rpc/impl_kombu.py +++ b/nova/openstack/common/rpc/impl_kombu.py @@ -198,6 +198,7 @@ class DirectConsumer(ConsumerBase): """ # Default options options = {'durable': False, + 'queue_arguments': _get_queue_arguments(conf), 'auto_delete': True, 'exclusive': False} options.update(kwargs) diff --git a/nova/openstack/common/rpc/impl_zmq.py b/nova/openstack/common/rpc/impl_zmq.py index 20a7f923d..c1cca34e8 100644 --- a/nova/openstack/common/rpc/impl_zmq.py +++ b/nova/openstack/common/rpc/impl_zmq.py @@ -216,12 +216,18 @@ class ZmqClient(object): socket_type = zmq.PUSH self.outq = ZmqSocket(addr, socket_type, bind=bind) - def cast(self, msg_id, topic, data, serialize=True, force_envelope=False): + def cast(self, msg_id, topic, data, envelope=False): msg_id = msg_id or 0 - if serialize: - data = rpc_common.serialize_msg(data, force_envelope) - self.outq.send(map(bytes, (msg_id, topic, 'cast', _serialize(data)))) + if not (envelope or rpc_common._SEND_RPC_ENVELOPE): + self.outq.send(map(bytes, + (msg_id, topic, 'cast', _serialize(data)))) + return + + rpc_envelope = rpc_common.serialize_msg(data[1], envelope) + zmq_msg = reduce(lambda x, y: x + y, rpc_envelope.items()) + self.outq.send(map(bytes, + (msg_id, topic, 'impl_zmq_v2', data[0]) + zmq_msg)) def close(self): self.outq.close() @@ -320,7 +326,7 @@ class ConsumerBase(object): else: return [result] - def process(self, style, target, proxy, ctx, data): + def process(self, proxy, ctx, data): data.setdefault('version', None) data.setdefault('args', {}) @@ -432,12 +438,14 @@ class ZmqProxy(ZmqBaseReactor): #TODO(ewindisch): use zero-copy (i.e. references, not copying) data = sock.recv() - msg_id, topic, style, in_msg = data - topic = topic.split('.', 1)[0] + topic = data[1] LOG.debug(_("CONSUMER GOT %s"), ' '.join(map(pformat, data))) - if topic.startswith('fanout~') or topic.startswith('zmq_replies'): + if topic.startswith('fanout~'): + sock_type = zmq.PUB + topic = topic.split('.', 1)[0] + elif topic.startswith('zmq_replies'): sock_type = zmq.PUB else: sock_type = zmq.PUSH @@ -520,6 +528,21 @@ class ZmqProxy(ZmqBaseReactor): super(ZmqProxy, self).consume_in_thread() +def unflatten_envelope(packenv): + """Unflattens the RPC envelope. + Takes a list and returns a dictionary. + i.e. [1,2,3,4] => {1: 2, 3: 4} + """ + i = iter(packenv) + h = {} + try: + while True: + k = i.next() + h[k] = i.next() + except StopIteration: + return h + + class ZmqReactor(ZmqBaseReactor): """ A consumer class implementing a @@ -540,38 +563,50 @@ class ZmqReactor(ZmqBaseReactor): self.mapping[sock].send(data) return - msg_id, topic, style, in_msg = data + proxy = self.proxies[sock] - ctx, request = rpc_common.deserialize_msg(_deserialize(in_msg)) - ctx = RpcContext.unmarshal(ctx) + if data[2] == 'cast': # Legacy protocol + packenv = data[3] - proxy = self.proxies[sock] + ctx, msg = _deserialize(packenv) + request = rpc_common.deserialize_msg(msg) + ctx = RpcContext.unmarshal(ctx) + elif data[2] == 'impl_zmq_v2': + packenv = data[4:] + + msg = unflatten_envelope(packenv) + request = rpc_common.deserialize_msg(msg) + + # Unmarshal only after verifying the message. + ctx = RpcContext.unmarshal(data[3]) + else: + LOG.error(_("ZMQ Envelope version unsupported or unknown.")) + return - self.pool.spawn_n(self.process, style, topic, - proxy, ctx, request) + self.pool.spawn_n(self.process, proxy, ctx, request) class Connection(rpc_common.Connection): """Manages connections and threads.""" def __init__(self, conf): + self.topics = [] self.reactor = ZmqReactor(conf) def create_consumer(self, topic, proxy, fanout=False): - # Only consume on the base topic name. - topic = topic.split('.', 1)[0] - - LOG.info(_("Create Consumer for topic (%(topic)s)") % - {'topic': topic}) - # Subscription scenarios if fanout: - subscribe = ('', fanout)[type(fanout) == str] sock_type = zmq.SUB - topic = 'fanout~' + topic + subscribe = ('', fanout)[type(fanout) == str] + topic = 'fanout~' + topic.split('.', 1)[0] else: sock_type = zmq.PULL subscribe = None + topic = '.'.join((topic.split('.', 1)[0], CONF.rpc_zmq_host)) + + if topic in self.topics: + LOG.info(_("Skipping topic registration. Already registered.")) + return # Receive messages from (local) proxy inaddr = "ipc://%s/zmq_topic_%s" % \ @@ -582,9 +617,11 @@ class Connection(rpc_common.Connection): self.reactor.register(proxy, inaddr, sock_type, subscribe=subscribe, in_bind=False) + self.topics.append(topic) def close(self): self.reactor.close() + self.topics = [] def wait(self): self.reactor.wait() @@ -593,8 +630,8 @@ class Connection(rpc_common.Connection): self.reactor.consume_in_thread() -def _cast(addr, context, topic, msg, timeout=None, serialize=True, - force_envelope=False, _msg_id=None): +def _cast(addr, context, topic, msg, timeout=None, envelope=False, + _msg_id=None): timeout_cast = timeout or CONF.rpc_cast_timeout payload = [RpcContext.marshal(context), msg] @@ -603,7 +640,7 @@ def _cast(addr, context, topic, msg, timeout=None, serialize=True, conn = ZmqClient(addr) # assumes cast can't return an exception - conn.cast(_msg_id, topic, payload, serialize, force_envelope) + conn.cast(_msg_id, topic, payload, envelope) except zmq.ZMQError: raise RPCException("Cast failed. ZMQ Socket Exception") finally: @@ -612,7 +649,7 @@ def _cast(addr, context, topic, msg, timeout=None, serialize=True, def _call(addr, context, topic, msg, timeout=None, - serialize=True, force_envelope=False): + envelope=False): # timeout_response is how long we wait for a response timeout = timeout or CONF.rpc_response_timeout @@ -642,20 +679,31 @@ def _call(addr, context, topic, msg, timeout=None, with Timeout(timeout, exception=rpc_common.Timeout): try: msg_waiter = ZmqSocket( - "ipc://%s/zmq_topic_zmq_replies" % CONF.rpc_zmq_ipc_dir, + "ipc://%s/zmq_topic_zmq_replies.%s" % + (CONF.rpc_zmq_ipc_dir, + CONF.rpc_zmq_host), zmq.SUB, subscribe=msg_id, bind=False ) LOG.debug(_("Sending cast")) - _cast(addr, context, topic, payload, - serialize=serialize, force_envelope=force_envelope) + _cast(addr, context, topic, payload, envelope) LOG.debug(_("Cast sent; Waiting reply")) # Blocks until receives reply msg = msg_waiter.recv() LOG.debug(_("Received message: %s"), msg) LOG.debug(_("Unpacking response")) - responses = _deserialize(msg[-1])[-1]['args']['response'] + + if msg[2] == 'cast': # Legacy version + raw_msg = _deserialize(msg[-1])[-1] + elif msg[2] == 'impl_zmq_v2': + rpc_envelope = unflatten_envelope(msg[4:]) + raw_msg = rpc_common.deserialize_msg(rpc_envelope) + else: + raise rpc_common.UnsupportedRpcEnvelopeVersion( + _("Unsupported or unknown ZMQ envelope returned.")) + + responses = raw_msg['args']['response'] # ZMQError trumps the Timeout error. except zmq.ZMQError: raise RPCException("ZMQ Socket Error") @@ -676,8 +724,8 @@ def _call(addr, context, topic, msg, timeout=None, return responses[-1] -def _multi_send(method, context, topic, msg, timeout=None, serialize=True, - force_envelope=False, _msg_id=None): +def _multi_send(method, context, topic, msg, timeout=None, + envelope=False, _msg_id=None): """ Wraps the sending of messages, dispatches to the matchmaker and sends @@ -703,11 +751,11 @@ def _multi_send(method, context, topic, msg, timeout=None, serialize=True, if method.__name__ == '_cast': eventlet.spawn_n(method, _addr, context, - _topic, msg, timeout, serialize, - force_envelope, _msg_id) + _topic, msg, timeout, envelope, + _msg_id) return return method(_addr, context, _topic, msg, timeout, - serialize, force_envelope) + envelope) def create_connection(conf, new=True): @@ -746,8 +794,7 @@ def notify(conf, context, topic, msg, **kwargs): # NOTE(ewindisch): dot-priority in rpc notifier does not # work with our assumptions. topic.replace('.', '-') - kwargs['serialize'] = kwargs.pop('envelope') - kwargs['force_envelope'] = True + kwargs['envelope'] = kwargs.get('envelope', True) cast(conf, context, topic, msg, **kwargs) diff --git a/nova/tests/api/openstack/compute/contrib/test_snapshots.py b/nova/tests/api/openstack/compute/contrib/test_snapshots.py index fa0c521fe..a890abe6f 100644 --- a/nova/tests/api/openstack/compute/contrib/test_snapshots.py +++ b/nova/tests/api/openstack/compute/contrib/test_snapshots.py @@ -18,71 +18,12 @@ import webob from nova.api.openstack.compute.contrib import volumes from nova import context -from nova import exception from nova.openstack.common import jsonutils -from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import test from nova.tests.api.openstack import fakes from nova.volume import cinder -LOG = logging.getLogger(__name__) - -_last_param = {} - - -def _get_default_snapshot_param(): - return { - 'id': 123, - 'volume_id': 12, - 'status': 'available', - 'volume_size': 100, - 'created_at': None, - 'display_name': 'Default name', - 'display_description': 'Default description', - } - - -def stub_snapshot_create(self, context, volume_id, name, description): - global _last_param - snapshot = _get_default_snapshot_param() - snapshot['volume_id'] = volume_id - snapshot['display_name'] = name - snapshot['display_description'] = description - - LOG.debug(_("_create: %s"), snapshot) - _last_param = snapshot - return snapshot - - -def stub_snapshot_delete(self, context, snapshot): - global _last_param - _last_param = snapshot - - LOG.debug(_("_delete: %s"), locals()) - if snapshot['id'] != '123': - raise exception.NotFound - - -def stub_snapshot_get(self, context, snapshot_id): - global _last_param - _last_param = dict(snapshot_id=snapshot_id) - - LOG.debug(_("_get: %s"), locals()) - if snapshot_id != '123': - raise exception.NotFound - - param = _get_default_snapshot_param() - param['id'] = snapshot_id - return param - - -def stub_snapshot_get_all(self, context): - LOG.debug(_("_get_all: %s"), locals()) - param = _get_default_snapshot_param() - param['id'] = 123 - return [param] - class SnapshotApiTest(test.TestCase): def setUp(self): @@ -90,14 +31,14 @@ class SnapshotApiTest(test.TestCase): fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) self.stubs.Set(cinder.API, "create_snapshot", - stub_snapshot_create) + fakes.stub_snapshot_create) self.stubs.Set(cinder.API, "create_snapshot_force", - stub_snapshot_create) + fakes.stub_snapshot_create) self.stubs.Set(cinder.API, "delete_snapshot", - stub_snapshot_delete) - self.stubs.Set(cinder.API, "get_snapshot", stub_snapshot_get) + fakes.stub_snapshot_delete) + self.stubs.Set(cinder.API, "get_snapshot", fakes.stub_snapshot_get) self.stubs.Set(cinder.API, "get_all_snapshots", - stub_snapshot_get_all) + fakes.stub_snapshot_get_all) self.stubs.Set(cinder.API, "get", fakes.stub_volume_get) self.flags( osapi_compute_extension=[ @@ -108,9 +49,6 @@ class SnapshotApiTest(test.TestCase): self.app = fakes.wsgi_app(init_only=('os-snapshots',)) def test_snapshot_create(self): - global _last_param - _last_param = {} - snapshot = {"volume_id": 12, "force": False, "display_name": "Snapshot Test Name", @@ -122,16 +60,8 @@ class SnapshotApiTest(test.TestCase): req.headers['content-type'] = 'application/json' resp = req.get_response(self.app) - LOG.debug(_("test_snapshot_create: param=%s"), _last_param) self.assertEqual(resp.status_int, 200) - - # Compare if parameters were correctly passed to stub - self.assertEqual(_last_param['display_name'], "Snapshot Test Name") - self.assertEqual(_last_param['display_description'], - "Snapshot Test Desc") - resp_dict = jsonutils.loads(resp.body) - LOG.debug(_("test_snapshot_create: resp_dict=%s"), resp_dict) self.assertTrue('snapshot' in resp_dict) self.assertEqual(resp_dict['snapshot']['displayName'], snapshot['display_name']) @@ -139,9 +69,6 @@ class SnapshotApiTest(test.TestCase): snapshot['display_description']) def test_snapshot_create_force(self): - global _last_param - _last_param = {} - snapshot = {"volume_id": 12, "force": True, "display_name": "Snapshot Test Name", @@ -153,16 +80,9 @@ class SnapshotApiTest(test.TestCase): req.headers['content-type'] = 'application/json' resp = req.get_response(self.app) - LOG.debug(_("test_snapshot_create_force: param=%s"), _last_param) self.assertEqual(resp.status_int, 200) - # Compare if parameters were correctly passed to stub - self.assertEqual(_last_param['display_name'], "Snapshot Test Name") - self.assertEqual(_last_param['display_description'], - "Snapshot Test Desc") - resp_dict = jsonutils.loads(resp.body) - LOG.debug(_("test_snapshot_create_force: resp_dict=%s"), resp_dict) self.assertTrue('snapshot' in resp_dict) self.assertEqual(resp_dict['snapshot']['displayName'], snapshot['display_name']) @@ -179,60 +99,41 @@ class SnapshotApiTest(test.TestCase): req.headers['content-type'] = 'application/json' resp = req.get_response(self.app) - LOG.debug(_("test_snapshot_create_force: param=%s"), _last_param) self.assertEqual(resp.status_int, 400) def test_snapshot_delete(self): - global _last_param - _last_param = {} - snapshot_id = 123 req = webob.Request.blank('/v2/fake/os-snapshots/%d' % snapshot_id) req.method = 'DELETE' resp = req.get_response(self.app) self.assertEqual(resp.status_int, 202) - self.assertEqual(str(_last_param['id']), str(snapshot_id)) def test_snapshot_delete_invalid_id(self): - global _last_param - _last_param = {} - - snapshot_id = 234 + snapshot_id = -1 req = webob.Request.blank('/v2/fake/os-snapshots/%d' % snapshot_id) req.method = 'DELETE' resp = req.get_response(self.app) self.assertEqual(resp.status_int, 404) - self.assertEqual(str(_last_param['snapshot_id']), str(snapshot_id)) def test_snapshot_show(self): - global _last_param - _last_param = {} - snapshot_id = 123 req = webob.Request.blank('/v2/fake/os-snapshots/%d' % snapshot_id) req.method = 'GET' resp = req.get_response(self.app) - LOG.debug(_("test_snapshot_show: resp=%s"), resp) self.assertEqual(resp.status_int, 200) - self.assertEqual(str(_last_param['snapshot_id']), str(snapshot_id)) - resp_dict = jsonutils.loads(resp.body) self.assertTrue('snapshot' in resp_dict) self.assertEqual(resp_dict['snapshot']['id'], str(snapshot_id)) def test_snapshot_show_invalid_id(self): - global _last_param - _last_param = {} - - snapshot_id = 234 + snapshot_id = -1 req = webob.Request.blank('/v2/fake/os-snapshots/%d' % snapshot_id) req.method = 'GET' resp = req.get_response(self.app) self.assertEqual(resp.status_int, 404) - self.assertEqual(str(_last_param['snapshot_id']), str(snapshot_id)) def test_snapshot_detail(self): req = webob.Request.blank('/v2/fake/os-snapshots/detail') @@ -241,13 +142,12 @@ class SnapshotApiTest(test.TestCase): self.assertEqual(resp.status_int, 200) resp_dict = jsonutils.loads(resp.body) - LOG.debug(_("test_snapshot_detail: resp_dict=%s"), resp_dict) self.assertTrue('snapshots' in resp_dict) resp_snapshots = resp_dict['snapshots'] - self.assertEqual(len(resp_snapshots), 1) + self.assertEqual(len(resp_snapshots), 3) resp_snapshot = resp_snapshots.pop() - self.assertEqual(resp_snapshot['id'], 123) + self.assertEqual(resp_snapshot['id'], 102) class SnapshotSerializerTest(test.TestCase): diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 7da10e73e..c906dae7f 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -597,11 +597,23 @@ def stub_snapshot(id, **kwargs): return snapshot -def stub_snapshot_get_all(self): +def stub_snapshot_create(self, context, volume_id, name, description): + return stub_snapshot(100, volume_id=volume_id, display_name=name, + display_description=description) + + +def stub_snapshot_delete(self, context, snapshot): + if snapshot['id'] == '-1': + raise exc.NotFound + + +def stub_snapshot_get(self, context, snapshot_id): + if snapshot_id == '-1': + raise exc.NotFound + return stub_snapshot(snapshot_id) + + +def stub_snapshot_get_all(self, context): return [stub_snapshot(100, project_id='fake'), stub_snapshot(101, project_id='superfake'), stub_snapshot(102, project_id='superduperfake')] - - -def stub_snapshot_get_all_by_project(self, context): - return [stub_snapshot(1)] diff --git a/nova/tests/baremetal/test_driver.py b/nova/tests/baremetal/test_driver.py index 8e23908f4..bf0a4740b 100644 --- a/nova/tests/baremetal/test_driver.py +++ b/nova/tests/baremetal/test_driver.py @@ -152,7 +152,7 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase): self.assertEqual(row['instance_uuid'], node['instance']['uuid']) self.assertEqual(row['instance_name'], node['instance']['hostname']) - def test_macs_for_instance(self): + def test_macs_from_nic_for_instance(self): node = self._create_node() expected = set([nic['address'] for nic in node['nic_info']]) self.assertEqual( diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index e19470db5..5ad333c9e 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -1420,6 +1420,54 @@ class ComputeTestCase(BaseTestCase): self.compute.terminate_instance(self.context, instance=instance) + def test_validate_console_port_vnc(self): + self.flags(vnc_enabled=True) + self.flags(enabled=True, group='spice') + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_vnc_console", + fake_driver_get_console) + + self.assertTrue(self.compute.validate_console_port(self.context, + instance, + "5900", + "novnc")) + + def test_validate_console_port_spice(self): + self.flags(vnc_enabled=True) + self.flags(enabled=True, group='spice') + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_spice_console", + fake_driver_get_console) + + self.assertTrue(self.compute.validate_console_port(self.context, + instance, + "5900", + "spice-html5")) + + def test_validate_console_port_wrong_port(self): + self.flags(vnc_enabled=True) + self.flags(enabled=True, group='spice') + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_vnc_console", + fake_driver_get_console) + + self.assertFalse(self.compute.validate_console_port(self.context, + instance, + "wrongport", + "spice-html5")) + def test_xvpvnc_vnc_console(self): # Make sure we can a vnc console for an instance. self.flags(vnc_enabled=True) @@ -1715,6 +1763,25 @@ class ComputeTestCase(BaseTestCase): instance=jsonutils.to_primitive(instance), bdms={}) + def test_delete_instance_deletes_console_auth_tokens(self): + instance = self._create_fake_instance() + self.flags(vnc_enabled=True) + + self.tokens_deleted = False + + def fake_delete_tokens(*args, **kwargs): + self.tokens_deleted = True + + cauth_rpcapi = self.compute.consoleauth_rpcapi + self.stubs.Set(cauth_rpcapi, 'delete_tokens_for_instance', + fake_delete_tokens) + + self.compute._delete_instance(self.context, + instance=jsonutils.to_primitive(instance), + bdms={}) + + self.assertTrue(self.tokens_deleted) + def test_instance_termination_exception_sets_error(self): """Test that we handle InstanceTerminationFailure which is propagated up from the underlying driver. @@ -2035,10 +2102,11 @@ class ComputeTestCase(BaseTestCase): new_iqn = 'iqn.2010-10.org.openstack:%s.2' % volume_id, new_connection_data['target_iqn'] = new_iqn - def fake_init_conn(self, context, volume, session): + def fake_init_conn_with_data(self, context, volume, session): connection_info['data'] = new_connection_data return connection_info - self.stubs.Set(cinder.API, "initialize_connection", fake_init_conn) + self.stubs.Set(cinder.API, "initialize_connection", + fake_init_conn_with_data) self.compute.finish_resize(self.context, migration=jsonutils.to_primitive(migration_ref), @@ -5735,7 +5803,8 @@ class ComputeAPITestCase(BaseTestCase): 'console_type': fake_console_type, 'host': 'fake_console_host', 'port': 'fake_console_port', - 'internal_access_path': 'fake_access_path'} + 'internal_access_path': 'fake_access_path', + 'instance_uuid': fake_instance['uuid']} fake_connect_info2 = copy.deepcopy(fake_connect_info) fake_connect_info2['access_url'] = 'fake_console_url' @@ -5747,7 +5816,7 @@ class ComputeAPITestCase(BaseTestCase): 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION} rpc_msg2 = {'method': 'authorize_console', 'args': fake_connect_info, - 'version': '1.0'} + 'version': '1.2'} rpc.call(self.context, 'compute.%s' % fake_instance['host'], rpc_msg1, None).AndReturn(fake_connect_info2) @@ -5779,7 +5848,8 @@ class ComputeAPITestCase(BaseTestCase): 'console_type': fake_console_type, 'host': 'fake_console_host', 'port': 'fake_console_port', - 'internal_access_path': 'fake_access_path'} + 'internal_access_path': 'fake_access_path', + 'instance_uuid': fake_instance['uuid']} fake_connect_info2 = copy.deepcopy(fake_connect_info) fake_connect_info2['access_url'] = 'fake_console_url' @@ -5791,7 +5861,7 @@ class ComputeAPITestCase(BaseTestCase): 'version': '2.24'} rpc_msg2 = {'method': 'authorize_console', 'args': fake_connect_info, - 'version': '1.0'} + 'version': '1.2'} rpc.call(self.context, 'compute.%s' % fake_instance['host'], rpc_msg1, None).AndReturn(fake_connect_info2) diff --git a/nova/tests/compute/test_rpcapi.py b/nova/tests/compute/test_rpcapi.py index a78a13883..1257f67e1 100644 --- a/nova/tests/compute/test_rpcapi.py +++ b/nova/tests/compute/test_rpcapi.py @@ -162,6 +162,9 @@ class ComputeRpcAPITestCase(test.TestCase): self._test_compute_api('get_diagnostics', 'call', instance=self.fake_instance) + def test_get_host_uptime(self): + self._test_compute_api('get_host_uptime', 'call') + def test_get_vnc_console(self): self._test_compute_api('get_vnc_console', 'call', instance=self.fake_instance, console_type='type') @@ -171,6 +174,12 @@ class ComputeRpcAPITestCase(test.TestCase): instance=self.fake_instance, console_type='type', version='2.24') + def test_validate_console_port(self): + self._test_compute_api('validate_console_port', 'call', + instance=self.fake_instance, port="5900", + console_type="novnc", + version="2.26") + def test_host_maintenance_mode(self): self._test_compute_api('host_maintenance_mode', 'call', host_param='param', mode='mode', host='host') diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py index ed733599b..32e685623 100644 --- a/nova/tests/conductor/test_conductor.py +++ b/nova/tests/conductor/test_conductor.py @@ -541,7 +541,7 @@ class _BaseTestCase(object): self.mox.ReplayAll() self.conductor.quota_commit(self.context, 'reservations') - def test_quota_commit(self): + def test_quota_rollback(self): self.mox.StubOutWithMock(quota.QUOTAS, 'rollback') quota.QUOTAS.rollback(self.context, 'reservations') self.mox.ReplayAll() diff --git a/nova/tests/consoleauth/test_consoleauth.py b/nova/tests/consoleauth/test_consoleauth.py index 15397a400..54e3d2261 100644 --- a/nova/tests/consoleauth/test_consoleauth.py +++ b/nova/tests/consoleauth/test_consoleauth.py @@ -42,12 +42,74 @@ class ConsoleauthTestCase(test.TestCase): self.useFixture(test.TimeOverride()) token = 'mytok' self.flags(console_token_ttl=1) + + def fake_validate_console_port(*args, **kwargs): + return True + self.stubs.Set(self.manager.compute_rpcapi, + "validate_console_port", + fake_validate_console_port) + self.manager.authorize_console(self.context, token, 'novnc', - '127.0.0.1', 'host', '') + '127.0.0.1', '8080', 'host', + 'instance') self.assertTrue(self.manager.check_token(self.context, token)) timeutils.advance_time_seconds(1) self.assertFalse(self.manager.check_token(self.context, token)) + def test_multiple_tokens_for_instance(self): + tokens = ["token" + str(i) for i in xrange(10)] + instance = "12345" + + def fake_validate_console_port(*args, **kwargs): + return True + + self.stubs.Set(self.manager.compute_rpcapi, + "validate_console_port", + fake_validate_console_port) + for token in tokens: + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance) + + for token in tokens: + self.assertTrue(self.manager.check_token(self.context, token)) + + def test_delete_tokens_for_instance(self): + instance = "12345" + tokens = ["token" + str(i) for i in xrange(10)] + for token in tokens: + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance) + self.manager.delete_tokens_for_instance(self.context, instance) + stored_tokens = self.manager._get_tokens_for_instance(instance) + + self.assertEqual(len(stored_tokens), 0) + + for token in tokens: + self.assertFalse(self.manager.check_token(self.context, token)) + + def test_wrong_token_has_port(self): + token = 'mytok' + + def fake_validate_console_port(*args, **kwargs): + return False + + self.stubs.Set(self.manager.compute_rpcapi, + "validate_console_port", + fake_validate_console_port) + + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance_uuid='instance') + self.assertFalse(self.manager.check_token(self.context, token)) + + def test_console_no_instance_uuid(self): + self.manager.authorize_console(self.context, "token", 'novnc', + '127.0.0.1', '8080', 'host', + instance_uuid=None) + self.assertFalse(self.manager.check_token(self.context, "token")) + def test_get_backdoor_port(self): self.manager.backdoor_port = 59697 port = self.manager.get_backdoor_port(self.context) diff --git a/nova/tests/consoleauth/test_rpcapi.py b/nova/tests/consoleauth/test_rpcapi.py index 15af5fdcf..53ca2e5d6 100644 --- a/nova/tests/consoleauth/test_rpcapi.py +++ b/nova/tests/consoleauth/test_rpcapi.py @@ -65,11 +65,17 @@ class ConsoleAuthRpcAPITestCase(test.TestCase): def test_authorize_console(self): self._test_consoleauth_api('authorize_console', token='token', console_type='ctype', host='h', port='p', - internal_access_path='iap') + internal_access_path='iap', instance_uuid="instance", + version="1.2") def test_check_token(self): self._test_consoleauth_api('check_token', token='t') + def test_delete_tokens_for_instnace(self): + self._test_consoleauth_api('delete_tokens_for_instance', + instance_uuid="instance", + version='1.2') + def test_get_backdoor_port(self): self._test_consoleauth_api('get_backdoor_port', host='fake_host', version='1.1') diff --git a/nova/tests/network/test_quantumv2.py b/nova/tests/network/test_quantumv2.py index b535363a8..e9e19ef45 100644 --- a/nova/tests/network/test_quantumv2.py +++ b/nova/tests/network/test_quantumv2.py @@ -1141,7 +1141,7 @@ class TestQuantumv2(test.TestCase): class TestQuantumv2ModuleMethods(test.TestCase): - def test_ensure_requested_network_ordering_no_preference(self): + def test_ensure_requested_network_ordering_no_preference_ids(self): l = [1, 2, 3] quantumapi._ensure_requested_network_ordering( @@ -1149,7 +1149,7 @@ class TestQuantumv2ModuleMethods(test.TestCase): l, None) - def test_ensure_requested_network_ordering_no_preference(self): + def test_ensure_requested_network_ordering_no_preference_hashes(self): l = [{'id': 3}, {'id': 1}, {'id': 2}] quantumapi._ensure_requested_network_ordering( diff --git a/nova/tests/test_libvirt_vif.py b/nova/tests/test_libvirt_vif.py index cb7943ea8..df0d951f7 100644 --- a/nova/tests/test_libvirt_vif.py +++ b/nova/tests/test_libvirt_vif.py @@ -409,7 +409,7 @@ class LibvirtVifTestCase(test.TestCase): self.mapping_bridge_quantum, br_want) - def _check_ovs_ethernet_driver(self, d, net, mapping): + def _check_ovs_ethernet_driver(self, d, net, mapping, dev_prefix): self.flags(firewall_driver="nova.virt.firewall.NoopFirewallDriver") xml = self._get_instance_xml(d, net, mapping) @@ -419,22 +419,22 @@ class LibvirtVifTestCase(test.TestCase): node = ret[0] self.assertEqual(node.get("type"), "ethernet") dev_name = node.find("target").get("dev") - self.assertTrue(dev_name.startswith("tap")) + self.assertTrue(dev_name.startswith(dev_prefix)) mac = node.find("mac").get("address") self.assertEqual(mac, self.mapping_ovs['mac']) script = node.find("script").get("path") self.assertEquals(script, "") - def test_ovs_ethernet_driver(self): + def test_ovs_ethernet_driver_legacy(self): def get_connection(): return fakelibvirt.Connection("qemu:///session", False, 9010) d = vif.LibvirtOpenVswitchDriver(get_connection) - d = vif.LibvirtOpenVswitchDriver() self._check_ovs_ethernet_driver(d, self.net_ovs, - self.mapping_ovs_legacy) + self.mapping_ovs_legacy, + "nic") def test_ovs_ethernet_driver(self): def get_connection(): @@ -444,7 +444,8 @@ class LibvirtVifTestCase(test.TestCase): d = vif.LibvirtGenericVIFDriver(get_connection) self._check_ovs_ethernet_driver(d, self.net_ovs, - self.mapping_ovs) + self.mapping_ovs, + "tap") def _check_ovs_virtualport_driver(self, d, net, mapping, want_iface_id): self.flags(firewall_driver="nova.virt.firewall.NoopFirewallDriver") diff --git a/nova/tests/virt/disk/test_nbd.py b/nova/tests/virt/disk/test_nbd.py index 750506882..858b13603 100644 --- a/nova/tests/virt/disk/test_nbd.py +++ b/nova/tests/virt/disk/test_nbd.py @@ -42,14 +42,6 @@ def _fake_listdir_nbd_devices(path): return ORIG_LISTDIR(path) -def _fake_exists_no_users(path): - if path.startswith('/sys/block/nbd'): - if path.endswith('pid'): - return False - return True - return ORIG_EXISTS(path) - - def _fake_exists_all_used(path): if path.startswith('/sys/block/nbd'): return True diff --git a/tools/run_pep8.sh b/tools/run_pep8.sh index 80c20a92d..a2a982cdc 100755 --- a/tools/run_pep8.sh +++ b/tools/run_pep8.sh @@ -24,4 +24,4 @@ PLUGINS_PATH=${SCRIPT_ROOT}/plugins/xenserver/xenapi/etc/xapi.d/plugins PYTHONPATH=${PLUGINS_PATH} ${PEP8} ./plugins/xenserver/xenapi \ `find plugins/xenserver/xenapi/etc/xapi.d/plugins -type f -perm +111` -! pyflakes nova/ | grep "imported but unused" +! pyflakes nova/ | grep "imported but unused\|redefinition of function" |