diff options
124 files changed, 1944 insertions, 1426 deletions
@@ -39,6 +39,7 @@ <masumotok@nttdata.co.jp> <root@openstack2-api> <matt.dietz@rackspace.com> <matthewdietz@Matthew-Dietzs-MacBook-Pro.local> <matt.dietz@rackspace.com> <mdietz@openstack> +<mikal@stillhq.com> <michael.still@canonical.com> <mordred@inaugust.com> <mordred@hudson> <naveedm9@gmail.com> <naveed.massjouni@rackspace.com> <rnirmal@gmail.com> <nirmal.ranganathan@rackspace.com> @@ -107,6 +107,7 @@ Lvov Maxim <usrleon@gmail.com> Mandell Degerness <mdegerne@gmail.com> Mark McLoughlin <markmc@redhat.com> Mark Washenberger <mark.washenberger@rackspace.com> +Maru Newby <mnewby@internap.com> Masanori Itoh <itoumsn@nttdata.co.jp> Matt Dietz <matt.dietz@rackspace.com> Matthew Hooker <matt@cloudscaling.com> @@ -169,3 +170,4 @@ Yuriy Taraday <yorik.sar@gmail.com> Zed Shaw <zedshaw@zedshaw.com> Zhixue Wu <Zhixue.Wu@citrix.com> Zhongyue Luo <lzyeval@gmail.com> +Ziad Sawalha <github@highbridgellc.com> diff --git a/bin/clear_rabbit_queues b/bin/clear_rabbit_queues index f697ef6b4..9d0d803a5 100755 --- a/bin/clear_rabbit_queues +++ b/bin/clear_rabbit_queues @@ -40,11 +40,11 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')): gettext.install('nova', unicode=1) -from nova.common import cfg from nova import context from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import rpc from nova import utils diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy index 7dd5266a7..94d93cd0e 100755 --- a/bin/nova-ajax-console-proxy +++ b/bin/nova-ajax-console-proxy @@ -38,9 +38,9 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): sys.path.insert(0, possible_topdir) -from nova.common import cfg from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import rpc from nova import service from nova import utils diff --git a/bin/nova-direct-api b/bin/nova-direct-api index f77311914..6a79ef626 100755 --- a/bin/nova-direct-api +++ b/bin/nova-direct-api @@ -35,11 +35,11 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): sys.path.insert(0, possible_topdir) -from nova.common import cfg from nova import compute from nova import flags from nova import log as logging from nova import network +from nova.openstack.common import cfg from nova import service from nova import utils from nova import volume diff --git a/bin/nova-manage b/bin/nova-manage index e89923d3c..6b15dea63 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -2307,7 +2307,16 @@ def methods_of(obj): def main(): """Parse options and call the appropriate class/method.""" - utils.default_flagfile() + flagfile = utils.default_flagfile() + + if flagfile and not os.access(flagfile, os.R_OK): + st = os.stat(flagfile) + print "Could not read %s. Re-running with sudo" % flagfile + try: + os.execvp('sudo', ['sudo', '-u', '#%s' % st.st_uid] + sys.argv) + except: + print 'sudo failed, continuing as if nothing happened' + argv = FLAGS(sys.argv) logging.setup() diff --git a/nova/api/auth.py b/nova/api/auth.py index 84ba3376d..316a8f72f 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -21,10 +21,10 @@ Common Auth Middleware. import webob.dec import webob.exc -from nova.common import cfg from nova import context from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import wsgi @@ -56,10 +56,10 @@ class NovaKeystoneContext(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): - try: - user_id = req.headers['X_USER'] - except KeyError: - logging.debug("X_USER not found in request") + user_id = req.headers.get('X_USER') + user_id = req.headers.get('X_USER_ID', user_id) + if user_id is None: + logging.debug("Neither X_USER_ID nor X_USER found in request") return webob.exc.HTTPUnauthorized() # get the roles roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index dfcddbc4e..bcd7b239e 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -32,11 +32,11 @@ from nova.api.ec2 import ec2utils from nova.api.ec2 import faults from nova.api import validator from nova.auth import manager -from nova.common import cfg from nova import context from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova import wsgi @@ -185,14 +185,27 @@ class EC2Token(wsgi.Middleware): # Not part of authentication args auth_params.pop('Signature') - # Authenticate the request. - creds = {'ec2Credentials': {'access': access, - 'signature': signature, - 'host': req.host, - 'verb': req.method, - 'path': req.path, - 'params': auth_params, - }} + if "ec2" in FLAGS.keystone_ec2_url: + LOG.warning("Configuration setting for keystone_ec2_url needs " + "to be updated to /tokens only. The /ec2 prefix is " + "being deprecated") + # Authenticate the request. + creds = {'ec2Credentials': {'access': access, + 'signature': signature, + 'host': req.host, + 'verb': req.method, + 'path': req.path, + 'params': auth_params, + }} + else: + # Authenticate the request. + creds = {'auth': {'OS-KSEC2:ec2Credentials': {'access': access, + 'signature': signature, + 'host': req.host, + 'verb': req.method, + 'path': req.path, + 'params': auth_params, + }}} creds_json = utils.dumps(creds) headers = {'Content-Type': 'application/json'} diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index b46a6c1eb..c19b8f103 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -457,14 +457,18 @@ class ViewBuilder(object): """Return href string with proper limit and marker params.""" params = request.params.copy() params["marker"] = identifier - url = os.path.join(request.application_url, + prefix = self._update_link_prefix(request.application_url, + FLAGS.osapi_compute_link_prefix) + url = os.path.join(prefix, request.environ["nova.context"].project_id, self._collection_name) return "%s?%s" % (url, dict_to_query_str(params)) def _get_href_link(self, request, identifier): """Return an href string pointing to this object.""" - return os.path.join(request.application_url, + prefix = self._update_link_prefix(request.application_url, + FLAGS.osapi_compute_link_prefix) + return os.path.join(prefix, request.environ["nova.context"].project_id, self._collection_name, str(identifier)) @@ -472,6 +476,8 @@ class ViewBuilder(object): def _get_bookmark_link(self, request, identifier): """Create a URL that refers to a specific resource.""" base_url = remove_version_from_href(request.application_url) + base_url = self._update_link_prefix(base_url, + FLAGS.osapi_compute_link_prefix) return os.path.join(base_url, request.environ["nova.context"].project_id, self._collection_name, @@ -492,3 +498,11 @@ class ViewBuilder(object): "href": self._get_next_link(request, last_item_id), }) return links + + def _update_link_prefix(self, orig_url, prefix): + if not prefix: + return orig_url + url_parts = list(urlparse.urlsplit(orig_url)) + prefix_parts = list(urlparse.urlsplit(prefix)) + url_parts[1] = prefix_parts[1] + return urlparse.urlunsplit(url_parts) diff --git a/nova/api/openstack/compute/__init__.py b/nova/api/openstack/compute/__init__.py index d035173ed..3884ffbad 100644 --- a/nova/api/openstack/compute/__init__.py +++ b/nova/api/openstack/compute/__init__.py @@ -31,9 +31,9 @@ from nova.api.openstack.compute import limits from nova.api.openstack.compute import servers from nova.api.openstack.compute import server_metadata from nova.api.openstack.compute import versions -from nova.common import cfg from nova import flags from nova import log as logging +from nova.openstack.common import cfg LOG = logging.getLogger('nova.api.openstack.compute') diff --git a/nova/api/openstack/compute/contrib/hosts.py b/nova/api/openstack/compute/contrib/hosts.py index 53f1a064a..6eee93e7b 100644 --- a/nova/api/openstack/compute/contrib/hosts.py +++ b/nova/api/openstack/compute/contrib/hosts.py @@ -68,6 +68,16 @@ class HostActionTemplate(xmlutil.TemplateBuilder): return xmlutil.MasterTemplate(root, 1) +class HostShowTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('host') + elem = xmlutil.make_flat_dict('resource', selector='host', + subselector='resource') + root.append(elem) + + return xmlutil.MasterTemplate(root, 1) + + class HostDeserializer(wsgi.XMLDeserializer): def default(self, string): try: @@ -83,15 +93,6 @@ class HostDeserializer(wsgi.XMLDeserializer): return dict(body=updates) -class HostShowTemplate(xmlutil.TemplateBuilder): - def construct(self): - root = xmlutil.TemplateElement('host', selector='host') - root.set('resource') - root.set('usage') - - return xmlutil.MasterTemplate(root, 1) - - def _list_hosts(req, service=None): """Returns a summary list of hosts, optionally filtering by service type. @@ -192,16 +193,10 @@ class HostController(object): :param context: security context :param host: hostname - :returns: - {'host': {'resource':D1, 'usage':{proj_id1:D2,..}}} - - 'resource' shows "available" and "in-use" vcpus, memory and disk. - 'usage' shows "in-use" vcpus, memory and disk per project. - - D1: {'vcpus': 16, 'memory_mb': 2048, 'local_gb': 2048, - 'vcpus_used': 12, 'memory_mb_used': 10240, - 'local_gb_used': 64} - D2: {'vcpus': 1, 'memory_mb': 2048, 'local_gb': 20} + :returns: expected to use HostShowTemplate. + ex. {'host': {'resource':D},..} + D: {'host': 'hostname','project': 'admin', + 'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30} """ host = id context = req.environ['nova.context'] @@ -222,16 +217,28 @@ class HostController(object): # Getting total available/used resource compute_ref = compute_ref['compute_node'][0] - resource = {'vcpus': compute_ref['vcpus'], - 'memory_mb': compute_ref['memory_mb'], - 'local_gb': compute_ref['local_gb'], - 'vcpus_used': compute_ref['vcpus_used'], - 'memory_mb_used': compute_ref['memory_mb_used'], - 'local_gb_used': compute_ref['local_gb_used']} - usage = dict() - if not instance_refs: - return {'host': - {'resource': resource, 'usage': usage}} + resources = [{'resource': {'host': host, 'project': '(total)', + 'cpu': compute_ref['vcpus'], + 'memory_mb': compute_ref['memory_mb'], + 'disk_gb': compute_ref['local_gb']}}, + {'resource': {'host': host, 'project': '(used_now)', + 'cpu': compute_ref['vcpus_used'], + 'memory_mb': compute_ref['memory_mb_used'], + 'disk_gb': compute_ref['local_gb_used']}}] + + cpu_sum = 0 + mem_sum = 0 + hdd_sum = 0 + for i in instance_refs: + cpu_sum += i['vcpus'] + mem_sum += i['memory_mb'] + hdd_sum += i['root_gb'] + i['ephemeral_gb'] + + resources.append({'resource': {'host': host, + 'project': '(used_max)', + 'cpu': cpu_sum, + 'memory_mb': mem_sum, + 'disk_gb': hdd_sum}}) # Getting usage resource per project project_ids = [i['project_id'] for i in instance_refs] @@ -246,11 +253,13 @@ class HostController(object): disk = [i['root_gb'] + i['ephemeral_gb'] for i in instance_refs if i['project_id'] == project_id] - usage[project_id] = {'vcpus': reduce(lambda x, y: x + y, vcpus), - 'memory_mb': reduce(lambda x, y: x + y, mem), - 'local_gb': reduce(lambda x, y: x + y, disk)} + resources.append({'resource': {'host': host, + 'project': project_id, + 'cpu': reduce(lambda x, y: x + y, vcpus), + 'memory_mb': reduce(lambda x, y: x + y, mem), + 'disk_gb': reduce(lambda x, y: x + y, disk)}}) - return {'host': {'resource': resource, 'usage': usage}} + return {'host': resources} class Hosts(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/compute/views/images.py b/nova/api/openstack/compute/views/images.py index 5ef1af59b..865e7df85 100644 --- a/nova/api/openstack/compute/views/images.py +++ b/nova/api/openstack/compute/views/images.py @@ -18,9 +18,13 @@ import os.path from nova.api.openstack import common +from nova import flags from nova import utils +FLAGS = flags.FLAGS + + class ViewBuilder(common.ViewBuilder): _collection_name = "images" @@ -109,6 +113,8 @@ class ViewBuilder(common.ViewBuilder): def _get_alternate_link(self, request, identifier): """Create an alternate link for a specific flavor id.""" glance_url = utils.generate_glance_url() + glance_url = self._update_link_prefix(glance_url, + FLAGS.osapi_glance_link_prefix) return os.path.join(glance_url, request.environ["nova.context"].project_id, self._collection_name, diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 79da5bd8d..3cb0de783 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -27,10 +27,10 @@ public methods. import functools import sys -from nova.common import cfg from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg ldap_opts = [ diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 234b9bf33..40f1fc842 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -30,13 +30,13 @@ import tempfile import uuid import zipfile -from nova.common import cfg from nova import context from nova import crypto from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.auth import signer diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py index 3c92f72b1..7d43866e6 100644 --- a/nova/cloudpipe/pipelib.py +++ b/nova/cloudpipe/pipelib.py @@ -27,13 +27,13 @@ import string import tempfile import zipfile -from nova.common import cfg from nova import context from nova import crypto from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils # TODO(eday): Eventually changes these to something not ec2-specific from nova.api.ec2 import cloud diff --git a/nova/compute/api.py b/nova/compute/api.py index 42c4ea4f0..f54d184dd 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -28,7 +28,6 @@ import novaclient import webob.exc from nova import block_device -from nova.common import cfg from nova.compute import aggregate_states from nova.compute import instance_types from nova.compute import power_state @@ -40,6 +39,7 @@ from nova import flags import nova.image from nova import log as logging from nova import network +from nova.openstack.common import cfg import nova.policy from nova import quota from nova import rpc diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 583b2da4e..e6a6d4a88 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -118,8 +118,8 @@ def get_default_instance_type(): name = FLAGS.default_instance_type try: return get_instance_type_by_name(name) - except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s") % name) + except exception.InstanceTypeNotFound as e: + raise exception.ApiError(e) def get_instance_type(instance_type_id): @@ -130,9 +130,8 @@ def get_instance_type(instance_type_id): ctxt = context.get_admin_context() try: return db.instance_type_get(ctxt, instance_type_id) - except exception.DBError: - msg = _("Unknown instance type: %s") % instance_type_id - raise exception.ApiError(msg) + except exception.InstanceTypeNotFound as e: + raise exception.ApiError(e) def get_instance_type_by_name(name): @@ -144,16 +143,16 @@ def get_instance_type_by_name(name): try: return db.instance_type_get_by_name(ctxt, name) - except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s") % name) + except exception.InstanceTypeNotFound as e: + raise exception.ApiError(e) # TODO(termie): flavor-specific code should probably be in the API that uses # flavors. def get_instance_type_by_flavor_id(flavorid): - """Retrieve instance type by flavorid.""" + """Retrieve instance type by flavorid. + + :raises: FlavorNotFound + """ ctxt = context.get_admin_context() - try: - return db.instance_type_get_by_flavor_id(ctxt, flavorid) - except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s") % flavorid) + return db.instance_type_get_by_flavor_id(ctxt, flavorid) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 57026d635..e5600af32 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -45,7 +45,6 @@ from eventlet import greenthread from nova import block_device import nova.context -from nova.common import cfg from nova.compute import instance_types from nova.compute import power_state from nova.compute import task_states @@ -59,6 +58,7 @@ from nova import manager from nova import network from nova.network import model as network_model from nova.notifier import api as notifier +from nova.openstack.common import cfg from nova import rpc from nova import utils from nova.virt import driver @@ -109,6 +109,13 @@ compute_opts = [ help="Action to take if a running deleted instance is detected." "Valid options are 'noop', 'log' and 'reap'. " "Set to 'noop' to disable."), + cfg.BoolOpt("use_image_cache_manager", + default=False, + help="Whether to manage images in the local cache."), + cfg.IntOpt("image_cache_manager_interval", + default=3600, + help="Number of periodic scheduler ticks to wait between " + "runs of the image cache manager."), ] FLAGS = flags.FLAGS @@ -195,6 +202,7 @@ class ComputeManager(manager.SchedulerDependentManager): self.network_manager = utils.import_object(FLAGS.network_manager) self._last_host_check = 0 self._last_bw_usage_poll = 0 + super(ComputeManager, self).__init__(service_name="compute", *args, **kwargs) @@ -644,9 +652,10 @@ class ComputeManager(manager.SchedulerDependentManager): # NOTE(vish): actual driver detach done in driver.destroy, so # just tell nova-volume that we are done with it. volume = self.volume_api.get(context, bdm['volume_id']) + connector = self.driver.get_volume_connector(instance) self.volume_api.terminate_connection(context, volume, - FLAGS.my_ip) + connector) self.volume_api.detach(context, volume) except exception.DiskNotFound as exc: LOG.warn(_("Ignoring DiskNotFound: %s") % exc) @@ -1589,10 +1598,10 @@ class ComputeManager(manager.SchedulerDependentManager): msg = _("instance %(instance_uuid)s: booting with " "volume %(volume_id)s at %(mountpoint)s") LOG.audit(msg % locals(), context=context) - address = FLAGS.my_ip + connector = self.driver.get_volume_connector(instance) connection_info = self.volume_api.initialize_connection(context, volume, - address) + connector) self.volume_api.attach(context, volume, instance_id, mountpoint) return connection_info @@ -1608,10 +1617,10 @@ class ComputeManager(manager.SchedulerDependentManager): msg = _("instance %(instance_uuid)s: attaching volume %(volume_id)s" " to %(mountpoint)s") LOG.audit(msg % locals(), context=context) - address = FLAGS.my_ip + connector = self.driver.get_volume_connector(instance_ref) connection_info = self.volume_api.initialize_connection(context, volume, - address) + connector) try: self.driver.attach_volume(connection_info, instance_ref['name'], @@ -1623,7 +1632,7 @@ class ComputeManager(manager.SchedulerDependentManager): LOG.exception(msg % locals(), context=context) self.volume_api.terminate_connection(context, volume, - address) + connector) self.volume_api.attach(context, volume, instance_id, mountpoint) values = { @@ -1666,7 +1675,8 @@ class ComputeManager(manager.SchedulerDependentManager): bdm = self._get_instance_volume_bdm(context, instance_id, volume_id) self._detach_volume(context, instance_ref, bdm) volume = self.volume_api.get(context, volume_id) - self.volume_api.terminate_connection(context, volume, FLAGS.my_ip) + connector = self.driver.get_volume_connector(instance_ref) + self.volume_api.terminate_connection(context, volume, connector) self.volume_api.detach(context.elevated(), volume) self.db.block_device_mapping_destroy_by_instance_and_volume( context, instance_id, volume_id) @@ -1685,7 +1695,8 @@ class ComputeManager(manager.SchedulerDependentManager): volume_id) self._detach_volume(context, instance_ref, bdm) volume = self.volume_api.get(context, volume_id) - self.volume_api.terminate_connection(context, volume, FLAGS.my_ip) + connector = self.driver.get_volume_connector(instance_ref) + self.volume_api.terminate_connection(context, volume, connector) except exception.NotFound: pass @@ -2077,10 +2088,14 @@ class ComputeManager(manager.SchedulerDependentManager): return for usage in bw_usage: - vif = usage['virtual_interface'] + mac = usage['mac_address'] + vif = self.network_api.get_vif_by_mac_address(context, mac) + if not vif: + continue + self.db.bw_usage_update(context, - vif.instance_id, - vif.network.label, + vif['instance_id'], + mac, start_time, usage['bw_in'], usage['bw_out']) @@ -2266,3 +2281,16 @@ class ComputeManager(manager.SchedulerDependentManager): def remove_aggregate_host(self, context, aggregate_id, host): """Removes a host from a physical hypervisor pool.""" raise NotImplementedError() + + @manager.periodic_task( + ticks_between_runs=FLAGS.image_cache_manager_interval) + def _run_image_cache_manager_pass(self, context): + """Run a single pass of the image cache manager.""" + + if not FLAGS.use_image_cache_manager: + return + + try: + self.driver.manage_image_cache(context) + except NotImplementedError: + pass diff --git a/nova/compute/utils.py b/nova/compute/utils.py index b8b34fa81..1358160e8 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -22,6 +22,8 @@ from nova import context from nova import db from nova import exception from nova import flags +from nova import network +from nova.network import model as network_model from nova.notifier import api as notifier_api from nova import utils @@ -44,10 +46,26 @@ def notify_usage_exists(instance_ref, current_period=False): else: audit_start = begin audit_end = end + + if (instance_ref.get('info_cache') and + instance_ref['info_cache'].get('network_info')): + + cached_info = instance_ref['info_cache']['network_info'] + nw_info = network_model.NetworkInfo.hydrate(cached_info) + else: + nw_info = network.API().get_instance_nw_info(admin_context, + instance_ref) + for b in db.bw_usage_get_by_instance(admin_context, instance_ref['id'], audit_start): - bw[b.network_label] = dict(bw_in=b.bw_in, bw_out=b.bw_out) + label = 'net-name-not-found-%s' % b['mac'] + for vif in nw_info: + if vif['address'] == b['mac']: + label = vif['network']['label'] + break + + bw[label] = dict(bw_in=b.bw_in, bw_out=b.bw_out) usage_info = utils.usage_from_instance(instance_ref, audit_period_beginning=str(audit_start), audit_period_ending=str(audit_end), diff --git a/nova/console/manager.py b/nova/console/manager.py index 220ac32c5..a181ee437 100644 --- a/nova/console/manager.py +++ b/nova/console/manager.py @@ -19,10 +19,10 @@ import socket -from nova.common import cfg from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import manager from nova import rpc from nova import utils diff --git a/nova/console/vmrc.py b/nova/console/vmrc.py index e29254d60..a104a5f7b 100644 --- a/nova/console/vmrc.py +++ b/nova/console/vmrc.py @@ -20,9 +20,9 @@ import base64 import json -from nova.common import cfg from nova import exception from nova import flags +from nova.openstack.common import cfg from nova.virt.vmwareapi import vim_util diff --git a/nova/console/vmrc_manager.py b/nova/console/vmrc_manager.py index 03cc46bf6..161499830 100644 --- a/nova/console/vmrc_manager.py +++ b/nova/console/vmrc_manager.py @@ -17,11 +17,11 @@ """VMRC Console Manager.""" -from nova.common import cfg from nova import exception from nova import flags from nova import log as logging from nova import manager +from nova.openstack.common import cfg from nova import rpc from nova import utils from nova.virt import vmwareapi_conn diff --git a/nova/console/xvp.py b/nova/console/xvp.py index 2b8dcc1e0..35e57f086 100644 --- a/nova/console/xvp.py +++ b/nova/console/xvp.py @@ -22,12 +22,12 @@ import signal from Cheetah import Template -from nova.common import cfg from nova import context from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils diff --git a/nova/consoleauth/__init__.py b/nova/consoleauth/__init__.py index 4048ae433..4b28776c0 100644 --- a/nova/consoleauth/__init__.py +++ b/nova/consoleauth/__init__.py @@ -18,8 +18,8 @@ """Module to authenticate Consoles.""" -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg consoleauth_topic_opt = \ diff --git a/nova/consoleauth/manager.py b/nova/consoleauth/manager.py index 08ddd5a3b..b3f85e1f9 100644 --- a/nova/consoleauth/manager.py +++ b/nova/consoleauth/manager.py @@ -22,10 +22,10 @@ import os import sys import time -from nova.common import cfg from nova import flags from nova import log as logging from nova import manager +from nova.openstack.common import cfg from nova import utils diff --git a/nova/crypto.py b/nova/crypto.py index a8b10de9b..79e40337b 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -22,25 +22,24 @@ Includes root and intermediate CAs, SSH key_pairs and x509 certificates. """ +from __future__ import absolute_import + import base64 -import gettext import hashlib import os import shutil import string import tempfile -import utils import Crypto.Cipher.AES -gettext.install('nova', unicode=1) - -from nova.common import cfg from nova import context from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg +from nova import utils LOG = logging.getLogger("nova.crypto") diff --git a/nova/db/api.py b/nova/db/api.py index de7b1de74..c73844a8c 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -43,9 +43,9 @@ these objects be simple dictionaries. """ -from nova.common import cfg from nova import exception from nova import flags +from nova.openstack.common import cfg from nova import utils @@ -1549,14 +1549,14 @@ def bw_usage_get_all_by_filters(context, filters): def bw_usage_update(context, instance_id, - network_label, + mac, start_period, bw_in, bw_out): """Update cached bw usage for an instance and network Creates new record if needed.""" return IMPL.bw_usage_update(context, instance_id, - network_label, + mac, start_period, bw_in, bw_out) diff --git a/nova/db/base.py b/nova/db/base.py index 77b7d82a7..a1b3bf711 100644 --- a/nova/db/base.py +++ b/nova/db/base.py @@ -18,9 +18,9 @@ """Base class for classes that need modular database access.""" -from nova.common import cfg from nova import utils from nova import flags +from nova.openstack.common import cfg db_driver_opt = \ diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 7a0f9b200..b27ac9689 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -3710,7 +3710,7 @@ def bw_usage_get_all_by_filters(context, filters): @require_context def bw_usage_update(context, instance_id, - network_label, + mac, start_period, bw_in, bw_out, session=None): @@ -3722,14 +3722,14 @@ def bw_usage_update(context, read_deleted="yes").\ filter_by(instance_id=instance_id).\ filter_by(start_period=start_period).\ - filter_by(network_label=network_label).\ + filter_by(mac=mac).\ first() if not bwusage: bwusage = models.BandwidthUsage() bwusage.instance_id = instance_id bwusage.start_period = start_period - bwusage.network_label = network_label + bwusage.mac = mac bwusage.last_refreshed = utils.utcnow() bwusage.bw_in = bw_in diff --git a/nova/db/sqlalchemy/migrate_repo/versions/075_convert_bw_usage_to_store_network_id.py b/nova/db/sqlalchemy/migrate_repo/versions/075_convert_bw_usage_to_store_network_id.py new file mode 100644 index 000000000..e75d6a6d9 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/075_convert_bw_usage_to_store_network_id.py @@ -0,0 +1,90 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import utils + + +meta = MetaData() + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + bw_usage_cache = Table('bw_usage_cache', meta, + Column('id', Integer, primary_key=True), + Column('network_label', String(255)), + Column('instance_id', Integer, nullable=False), + Column('start_period', DateTime, nullable=False), + Column('last_refreshed', DateTime), + Column('bw_in', BigInteger), + Column('bw_out', BigInteger), + Column('created_at', DateTime(timezone=False), + default=utils.utcnow()), + Column('updated_at', DateTime(timezone=False), + onupdate=utils.utcnow()), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, + name=None))) + + vifs = Table('virtual_interfaces', meta, autoload=True) + networks = Table('networks', meta, autoload=True) + mac_column = Column('mac', String(255)) + + bw_usage_cache.create_column(mac_column) + + bw_usage_cache.update()\ + .values(mac=select([vifs.c.address])\ + .where(and_(networks.c.label == bw_usage_cache.c.network_label, + networks.c.id == vifs.c.network_id))\ + .as_scalar()).execute() + + bw_usage_cache.c.network_label.drop() + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + bw_usage_cache = Table('bw_usage_cache', meta, + Column('id', Integer, primary_key=True), + Column('network_uuid', String(36)), + Column('instance_id', Integer, nullable=False), + Column('start_period', DateTime, nullable=False), + Column('last_refreshed', DateTime), + Column('bw_in', BigInteger), + Column('bw_out', BigInteger), + Column('created_at', DateTime(timezone=False), + default=utils.utcnow()), + Column('updated_at', DateTime(timezone=False), + onupdate=utils.utcnow()), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, + name=None))) + + vifs = Table('virtual_interfaces', meta, autoload=True) + network = Table('networks', meta, autoload=True) + network_label_column = Column('network_label', String(255)) + + bw_usage_cache.create_column(network_label_column) + + bw_usage_cache.update()\ + .values(network_label=select([network.c.label])\ + .where(and_(network.c.id == vifs.c.network_id, + vifs.c.address == bw_usage_cache.c.mac))\ + .as_scalar()).execute() + + bw_usage_cache.c.network_uuid.drop() diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index f7b9a555c..df7f42c8d 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -927,7 +927,7 @@ class BandwidthUsage(BASE, NovaBase): __tablename__ = 'bw_usage_cache' id = Column(Integer, primary_key=True, nullable=False) instance_id = Column(Integer, nullable=False) - network_label = Column(String(255)) + mac = Column(String(255), nullable=False) start_period = Column(DateTime, nullable=False) last_refreshed = Column(DateTime) bw_in = Column(BigInteger) diff --git a/nova/flags.py b/nova/flags.py index 4c0d61498..3f3560057 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -32,7 +32,7 @@ import sys import gflags -from nova.common import cfg +from nova.openstack.common import cfg class FlagValues(object): @@ -338,6 +338,14 @@ global_opts = [ cfg.StrOpt('osapi_path', default='/v1.1/', help='suffix for openstack'), + cfg.StrOpt('osapi_compute_link_prefix', + default=None, + help='Base URL that will be presented to users in links ' + 'to the Openstack Compute API'), + cfg.StrOpt('osapi_glance_link_prefix', + default=None, + help='Base URL that will be presented to users in links ' + 'to glance resources'), cfg.IntOpt('osapi_max_limit', default=1000, help='max number of items returned in a collection response'), @@ -442,7 +450,7 @@ global_opts = [ default=100, help='default partition size for shared capacity'), cfg.StrOpt('firewall_driver', - default='nova.virt.libvirt.firewall.IptablesFirewallDriver', + default='nova.virt.firewall.IptablesFirewallDriver', help='Firewall driver (defaults to iptables)'), cfg.StrOpt('image_service', default='nova.image.glance.GlanceImageService', diff --git a/nova/image/s3.py b/nova/image/s3.py index ee11c163c..2be1b5ede 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -31,11 +31,11 @@ import eventlet from nova import rpc import nova.db.api -from nova.common import cfg from nova import exception from nova import flags from nova import image from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.api.ec2 import ec2utils diff --git a/nova/ipv6/api.py b/nova/ipv6/api.py index 5931b8ba6..ea6d8eb22 100644 --- a/nova/ipv6/api.py +++ b/nova/ipv6/api.py @@ -14,8 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg from nova import utils diff --git a/nova/log.py b/nova/log.py index c0be3b95b..c0bc256cc 100644 --- a/nova/log.py +++ b/nova/log.py @@ -40,9 +40,9 @@ import sys import traceback import nova -from nova.common import cfg from nova import flags from nova import local +from nova.openstack.common import cfg from nova import version @@ -63,10 +63,6 @@ log_opts = [ cfg.StrOpt('logging_exception_prefix', default='(%(name)s): TRACE: ', help='prefix each line of exception output with this format'), - cfg.StrOpt('logging_debug_format_suffix', - default='from (pid=%(process)d) %(funcName)s ' - '%(pathname)s:%(lineno)d', - help='data to append to log format when level is DEBUG'), cfg.StrOpt('instance_format', default='[instance: %(uuid)s] ', help='If an instance is passed with the log message, format ' diff --git a/nova/network/api.py b/nova/network/api.py index 257c642a9..27e07b869 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -99,6 +99,12 @@ class API(base.Base): {'method': 'get_vifs_by_instance', 'args': {'instance_id': instance['id']}}) + def get_vif_by_mac_address(self, context, mac_address): + return rpc.call(context, + FLAGS.network_topic, + {'method': 'get_vif_by_mac_address', + 'args': {'mac_address': mac_address}}) + def allocate_floating_ip(self, context, pool=None): """Adds a floating ip to a project from a pool. (allocates)""" # NOTE(vish): We don't know which network host should get the ip diff --git a/nova/network/ldapdns.py b/nova/network/ldapdns.py index a5491a259..a6d921186 100644 --- a/nova/network/ldapdns.py +++ b/nova/network/ldapdns.py @@ -19,10 +19,10 @@ import tempfile import time from nova.auth import fakeldap -from nova.common import cfg from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg LOG = logging.getLogger("nova.network.manager") diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 56d07f983..fdfcffb86 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -24,11 +24,11 @@ import inspect import netaddr import os -from nova.common import cfg from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils @@ -944,10 +944,11 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): def plug(self, network, mac_address, gateway=True): if network.get('vlan', None) is not None: + iface = FLAGS.vlan_interface or network['bridge_interface'] LinuxBridgeInterfaceDriver.ensure_vlan_bridge( network['vlan'], network['bridge'], - network['bridge_interface'], + iface, network, mac_address) else: diff --git a/nova/network/manager.py b/nova/network/manager.py index c6d3122b6..053305992 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -56,7 +56,6 @@ import socket from eventlet import greenpool import netaddr -from nova.common import cfg from nova.compute import api as compute_api from nova.compute import instance_types from nova import context @@ -68,6 +67,7 @@ from nova import log as logging from nova import manager from nova.network import api as network_api from nova.network import model as network_model +from nova.openstack.common import cfg import nova.policy from nova import quota from nova import utils @@ -1476,6 +1476,11 @@ class NetworkManager(manager.SchedulerDependentManager): fixed = self.db.fixed_ip_get(context, id) return dict(fixed.iteritems()) + def get_vif_by_mac_address(self, context, mac_address): + """Returns the vifs record for the mac_address""" + return self.db.virtual_interface_get_by_address(context, + mac_address) + class FlatManager(NetworkManager): """Basic network where no vlans are used. diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index 2e5d6f707..edd214326 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -44,12 +44,16 @@ class JSONSerializer(object): def deserialize(self, data, content_type): return json.loads(data) +# Quantum API v1.0 uses 420 + 430 for network + port not found +# Quantum API v1.1 uses 404 for network + port not found +NOT_FOUND_CODES = set((404, 420, 430)) + # The full client lib will expose more # granular exceptions, for now, just try to distinguish # between the cases we care about. class QuantumNotFoundException(Exception): - """Indicates that Quantum Server returned 404""" + """Indicates that Quantum Server returned a not-found error code""" pass @@ -90,7 +94,7 @@ class api_call(object): class Client(object): """A base client class - derived from Glance.BaseClient""" - action_prefix = '/v1.0/tenants/{tenant_id}' + action_prefix = '/v1.1/tenants/{tenant_id}' """Action query strings""" networks_path = "/networks" @@ -188,7 +192,7 @@ class Client(object): self.logger.debug("Quantum Client Reply (code = %s) :\n %s" \ % (str(status_code), data)) - if status_code == httplib.NOT_FOUND: + if status_code in NOT_FOUND_CODES: raise QuantumNotFoundException( _("Quantum entity not found: %s" % data)) @@ -233,10 +237,18 @@ class Client(object): format = self.format return "application/%s" % (format) + def append_filter_params(self, url, filter_ops): + if len(filter_ops) > 0: + url += "?" + for fkey, fval in filter_ops.values(): + url += "%s=%s&" % (fkey, fval) + @api_call - def list_networks(self): + def list_networks(self, filter_ops={}): """Fetches a list of all networks for a tenant""" - return self.do_request("GET", self.networks_path) + url = self.networks_path + self.append_filter_params(url, filter_ops) + return self.do_request("GET", url) @api_call def show_network_details(self, network): @@ -261,9 +273,11 @@ class Client(object): return self.do_request("DELETE", self.network_path % (network)) @api_call - def list_ports(self, network): + def list_ports(self, network, filter_ops={}): """Fetches a list of ports on a given network""" - return self.do_request("GET", self.ports_path % (network)) + url = self.ports_path % (network) + self.append_filter_params(url, filter_ops) + return self.do_request("GET", url) @api_call def show_port_details(self, network, port): diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 518676be0..8f2aa0a4a 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -19,7 +19,6 @@ import time from netaddr import IPNetwork, IPAddress -from nova.common import cfg from nova.compute import instance_types from nova import context from nova import db @@ -29,6 +28,7 @@ from nova import log as logging from nova.network import manager from nova.network.quantum import melange_ipam_lib from nova.network.quantum import quantum_connection +from nova.openstack.common import cfg from nova import utils LOG = logging.getLogger("nova.network.quantum.manager") @@ -474,11 +474,12 @@ class QuantumManager(manager.FloatingIP, manager.FlatManager): if vif.get('network_id') is not None: network = db.network_get(admin_context, vif['network_id']) net_tenant_id = net_tenant_dict[network['uuid']] + if net_tenant_id is None: + net_tenant_id = FLAGS.quantum_default_tenant_id network = {'id': network['id'], 'uuid': network['uuid'], - 'bridge': 'ovs_flag', - 'label': self.q_conn.get_network_name(net_tenant_id, - network['uuid']), + 'bridge': '', # Quantum ignores this field + 'label': network['label'], 'project_id': net_tenant_id} networks[vif['uuid']] = network @@ -538,6 +539,7 @@ class QuantumManager(manager.FloatingIP, manager.FlatManager): self.ipam.deallocate_ips_by_vif(context, ipam_tenant_id, net_id, vif_ref) + db.virtual_interface_delete(admin_context, vif_ref['id']) # If DHCP is enabled on this network then we need to update the # leases and restart the server. @@ -551,13 +553,6 @@ class QuantumManager(manager.FloatingIP, manager.FlatManager): '|%(instance_id)s|, vif_uuid: |%(vif_uuid)s|') LOG.critical(msg % locals) - try: - db.virtual_interface_delete_by_instance(admin_context, - instance_id) - except exception.InstanceNotFound: - LOG.error(_("Attempted to deallocate non-existent instance: %s" % - (instance_id))) - # TODO(bgh): At some point we should consider merging enable_dhcp() and # update_dhcp() # TODO(tr3buchet): agree, i'm curious why they differ even now.. diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py index 499ace6b3..c7e3c606d 100644 --- a/nova/network/quantum/melange_connection.py +++ b/nova/network/quantum/melange_connection.py @@ -20,9 +20,9 @@ import socket import urllib import json -from nova.common import cfg from nova import flags from nova import log as logging +from nova.openstack.common import cfg melange_opts = [ diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index 914b816d1..ba34d026c 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -15,10 +15,10 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.common import cfg from nova import flags from nova import log as logging from nova.network.quantum import client as quantum_client +from nova.openstack.common import cfg LOG = logging.getLogger("nova.network.quantum.quantum_connection") @@ -117,18 +117,21 @@ class QuantumClientConnection(object): """Given a tenant and network, search for the port UUID that has the specified interface-id attachment. """ - # FIXME(danwent): this will be inefficient until the Quantum - # API implements querying a port by the interface-id - port_list_resdict = self.client.list_ports(net_id, tenant=tenant_id) - for p in port_list_resdict["ports"]: - port_id = p["id"] - port_get_resdict = self.client.show_port_attachment(net_id, - port_id, tenant=tenant_id) - # Skip ports without an attachment - if "id" not in port_get_resdict["attachment"]: - continue - if attachment_id == port_get_resdict["attachment"]["id"]: - return port_id + port_list = [] + try: + port_list_resdict = self.client.list_ports(net_id, + tenant=tenant_id, + filter_ops={'attachment': attachment_id}) + port_list = port_list_resdict["ports"] + except quantum_client.QuantumNotFoundException: + return None + + port_list_len = len(port_list) + if port_list_len != 1: + LOG.error("Expected single port with attachment " + "%(attachment_id)s, found %(port_list_len)s" % locals()) + if port_list_len >= 1: + return port_list[0]['id'] return None def get_attached_ports(self, tenant_id, network_id): diff --git a/nova/notifier/api.py b/nova/notifier/api.py index 50730cb0f..4a59e0ba1 100644 --- a/nova/notifier/api.py +++ b/nova/notifier/api.py @@ -15,10 +15,10 @@ import uuid -from nova.common import cfg from nova import flags from nova import utils from nova import log as logging +from nova.openstack.common import cfg LOG = logging.getLogger('nova.exception') diff --git a/nova/notifier/list_notifier.py b/nova/notifier/list_notifier.py index 29c6ba720..d952b613e 100644 --- a/nova/notifier/list_notifier.py +++ b/nova/notifier/list_notifier.py @@ -13,9 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.common import cfg from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.exception import ClassNotFound diff --git a/nova/notifier/rabbit_notifier.py b/nova/notifier/rabbit_notifier.py index c88f76cb0..25956c611 100644 --- a/nova/notifier/rabbit_notifier.py +++ b/nova/notifier/rabbit_notifier.py @@ -16,8 +16,8 @@ import nova.context -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg from nova import rpc diff --git a/nova/objectstore/s3server.py b/nova/objectstore/s3server.py index 1ba8d44cf..e5217b4b9 100644 --- a/nova/objectstore/s3server.py +++ b/nova/objectstore/s3server.py @@ -44,9 +44,9 @@ import urllib import routes import webob -from nova.common import cfg from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova import wsgi diff --git a/nova/openstack/__init__.py b/nova/openstack/__init__.py new file mode 100644 index 000000000..0a3b98867 --- /dev/null +++ b/nova/openstack/__init__.py @@ -0,0 +1,15 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Red Hat, 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. diff --git a/nova/openstack/common/__init__.py b/nova/openstack/common/__init__.py new file mode 100644 index 000000000..0a3b98867 --- /dev/null +++ b/nova/openstack/common/__init__.py @@ -0,0 +1,15 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Red Hat, 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. diff --git a/nova/common/cfg.py b/nova/openstack/common/cfg.py index bfb7b8a19..bfb7b8a19 100644 --- a/nova/common/cfg.py +++ b/nova/openstack/common/cfg.py diff --git a/nova/policy.py b/nova/policy.py index ccf1f4624..09b3ab988 100644 --- a/nova/policy.py +++ b/nova/policy.py @@ -17,10 +17,10 @@ """Policy Engine For Nova""" -from nova.common import cfg from nova.common import policy from nova import exception from nova import flags +from nova.openstack.common import cfg from nova import utils diff --git a/nova/quota.py b/nova/quota.py index 95fa317d5..4a5eac4b4 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -18,8 +18,8 @@ """Quotas for instances, volumes, and floating ips.""" -from nova.common import cfg from nova import db +from nova.openstack.common import cfg from nova import flags @@ -110,9 +110,13 @@ def allowed_instances(context, requested_instances, instance_type): allowed_cores = _get_request_allotment(requested_cores, used_cores, quota['cores']) allowed_ram = _get_request_allotment(requested_ram, used_ram, quota['ram']) - allowed_instances = min(allowed_instances, - allowed_cores // instance_type['vcpus'], - allowed_ram // instance_type['memory_mb']) + if instance_type['vcpus']: + allowed_instances = min(allowed_instances, + allowed_cores // instance_type['vcpus']) + if instance_type['memory_mb']: + allowed_instances = min(allowed_instances, + allowed_ram // instance_type['memory_mb']) + return min(requested_instances, allowed_instances) diff --git a/nova/rootwrap/compute.py b/nova/rootwrap/compute.py index 8b7736cbd..65e6dfebb 100755 --- a/nova/rootwrap/compute.py +++ b/nova/rootwrap/compute.py @@ -168,4 +168,7 @@ filterlist = [ # nova/virt/libvirt/utils.py: 'mkswap' # nova/virt/xenapi/vm_utils.py: 'mkswap' filters.CommandFilter("/sbin/mkswap", "root"), + + # nova/virt/libvirt/connection.py: + filters.ReadFileFilter("/etc/iscsi/initiatorname.iscsi"), ] diff --git a/nova/rootwrap/filters.py b/nova/rootwrap/filters.py index d16fc9a57..faaeb11f7 100755 --- a/nova/rootwrap/filters.py +++ b/nova/rootwrap/filters.py @@ -123,3 +123,20 @@ class KillFilter(CommandFilter): # Incorrect PID return False return True + + +class ReadFileFilter(CommandFilter): + """Specific filter for the utils.read_file_as_root call""" + + def __init__(self, file_path, *args): + self.file_path = file_path + super(ReadFileFilter, self).__init__("/bin/cat", "root", *args) + + def match(self, userargs): + if userargs[0] != 'cat': + return False + if userargs[1] != self.file_path: + return False + if len(userargs) != 2: + return False + return True diff --git a/nova/rpc/__init__.py b/nova/rpc/__init__.py index a6067432e..7c6ed29d0 100644 --- a/nova/rpc/__init__.py +++ b/nova/rpc/__init__.py @@ -17,10 +17,10 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.common import cfg -from nova.utils import import_object -from nova.rpc.common import RemoteError, LOG from nova import flags +from nova.openstack.common import cfg +from nova.rpc.common import RemoteError, LOG +from nova.utils import import_object rpc_backend_opt = \ diff --git a/nova/rpc/common.py b/nova/rpc/common.py index 70d5d07ba..696639e03 100644 --- a/nova/rpc/common.py +++ b/nova/rpc/common.py @@ -19,10 +19,10 @@ import copy -from nova.common import cfg from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg LOG = logging.getLogger('nova.rpc') diff --git a/nova/rpc/impl_qpid.py b/nova/rpc/impl_qpid.py index 353c7e502..1ed20ba81 100644 --- a/nova/rpc/impl_qpid.py +++ b/nova/rpc/impl_qpid.py @@ -25,8 +25,8 @@ import greenlet import qpid.messaging import qpid.messaging.exceptions -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg from nova.rpc import amqp as rpc_amqp from nova.rpc import common as rpc_common from nova.rpc.common import LOG diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index c814ad664..814e45c6f 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -22,11 +22,11 @@ import functools from novaclient import v1_1 as novaclient from novaclient import exceptions as novaclient_exceptions -from nova.common import cfg from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import rpc from nova import utils diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py index 5af98485d..f73eebc52 100644 --- a/nova/scheduler/driver.py +++ b/nova/scheduler/driver.py @@ -22,7 +22,6 @@ Scheduler base class that all Schedulers should inherit from """ from nova.api.ec2 import ec2utils -from nova.common import cfg from nova.compute import api as compute_api from nova.compute import power_state from nova.compute import vm_states @@ -30,6 +29,7 @@ from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import rpc from nova.scheduler import host_manager from nova.scheduler import zone_manager diff --git a/nova/scheduler/filters/core_filter.py b/nova/scheduler/filters/core_filter.py index d76eceeef..5afc63c24 100644 --- a/nova/scheduler/filters/core_filter.py +++ b/nova/scheduler/filters/core_filter.py @@ -15,9 +15,9 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.common import cfg from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova.scheduler.filters import abstract_filter diff --git a/nova/scheduler/filters/ram_filter.py b/nova/scheduler/filters/ram_filter.py index 67c9f72b8..8bae04949 100644 --- a/nova/scheduler/filters/ram_filter.py +++ b/nova/scheduler/filters/ram_filter.py @@ -14,9 +14,9 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.common import cfg from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova.scheduler.filters import abstract_filter LOG = logging.getLogger('nova.scheduler.filter.ram_filter') diff --git a/nova/scheduler/host_manager.py b/nova/scheduler/host_manager.py index a56f154fc..925a8637a 100644 --- a/nova/scheduler/host_manager.py +++ b/nova/scheduler/host_manager.py @@ -21,11 +21,11 @@ import datetime import types import UserDict -from nova.common import cfg from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils diff --git a/nova/scheduler/least_cost.py b/nova/scheduler/least_cost.py index 90ba4c2a0..4711ac4f3 100644 --- a/nova/scheduler/least_cost.py +++ b/nova/scheduler/least_cost.py @@ -22,8 +22,8 @@ The cost-function and weights are tabulated, and the host with the least cost is then selected for provisioning. """ -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg from nova import log as logging diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index dc1b2c56a..e4eb08d11 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -23,13 +23,13 @@ Scheduler Service import functools -from nova.common import cfg from nova.compute import vm_states from nova import db from nova import exception from nova import flags from nova import log as logging from nova import manager +from nova.openstack.common import cfg from nova import rpc from nova import utils diff --git a/nova/scheduler/multi.py b/nova/scheduler/multi.py index 3bc67052f..08d4bf0e6 100644 --- a/nova/scheduler/multi.py +++ b/nova/scheduler/multi.py @@ -21,8 +21,8 @@ Scheduler that allows routing some calls to one driver and others to another. """ -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg from nova import utils from nova.scheduler import driver diff --git a/nova/scheduler/scheduler_options.py b/nova/scheduler/scheduler_options.py index 0a6086fc6..63a77d08d 100644 --- a/nova/scheduler/scheduler_options.py +++ b/nova/scheduler/scheduler_options.py @@ -24,9 +24,9 @@ import datetime import json import os -from nova.common import cfg from nova import flags from nova import log as logging +from nova.openstack.common import cfg scheduler_json_config_location_opt = \ diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py index e913730c7..94db0a475 100644 --- a/nova/scheduler/simple.py +++ b/nova/scheduler/simple.py @@ -21,10 +21,10 @@ Simple Scheduler """ -from nova.common import cfg from nova import db from nova import flags from nova import exception +from nova.openstack.common import cfg from nova.scheduler import driver from nova.scheduler import chance from nova import utils diff --git a/nova/scheduler/vsa.py b/nova/scheduler/vsa.py index 989841d37..ee50ae978 100644 --- a/nova/scheduler/vsa.py +++ b/nova/scheduler/vsa.py @@ -19,11 +19,11 @@ VSA Simple Scheduler """ -from nova.common import cfg from nova import context from nova import db from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import rpc from nova import utils from nova import exception diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index c27e6bdbe..e94039a6b 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -23,10 +23,10 @@ import traceback from eventlet import greenpool from novaclient import v1_1 as novaclient -from nova.common import cfg from nova import db from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils diff --git a/nova/service.py b/nova/service.py index 56f706c4b..fc62a3b08 100644 --- a/nova/service.py +++ b/nova/service.py @@ -25,12 +25,12 @@ import os import eventlet import greenlet -from nova.common import cfg from nova import context from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import rpc from nova import utils from nova import version diff --git a/nova/test.py b/nova/test.py index f8e9c390f..7fe4b4abb 100644 --- a/nova/test.py +++ b/nova/test.py @@ -33,10 +33,10 @@ import mox import nose.plugins.skip import stubout -from nova.common import cfg from nova import flags import nova.image.fake from nova import log +from nova.openstack.common import cfg from nova import utils from nova import service from nova.testing.fake import rabbit diff --git a/nova/tests/api/openstack/compute/contrib/test_hosts.py b/nova/tests/api/openstack/compute/contrib/test_hosts.py index 925dc0d53..a16b8a5f5 100644 --- a/nova/tests/api/openstack/compute/contrib/test_hosts.py +++ b/nova/tests/api/openstack/compute/contrib/test_hosts.py @@ -184,17 +184,6 @@ class HostTestCase(test.TestCase): self.controller.show, self.req, dest) - def _dic_is_equal(self, dic1, dic2, keys=None): - """Compares 2 dictionary contents(Helper method)""" - if not keys: - keys = ['vcpus', 'memory_mb', 'local_gb', - 'vcpus_used', 'memory_mb_used', 'local_gb_used'] - - for key in keys: - if not (dic1[key] == dic2[key]): - return False - return True - def _create_compute_service(self): """Create compute-manager(ComputeNode and Service record).""" ctxt = context.get_admin_context() @@ -218,14 +207,13 @@ class HostTestCase(test.TestCase): result = self.controller.show(self.req, s_ref['host']) - # result checking - c1 = ('resource' in result['host'] and - 'usage' in result['host']) - compute_node = s_ref['compute_node'][0] - c2 = self._dic_is_equal(result['host']['resource'], - compute_node) - c3 = result['host']['usage'] == {} - self.assertTrue(c1 and c2 and c3) + proj = ['(total)', '(used_now)', '(used_max)'] + column = ['host', 'project', 'cpu', 'memory_mb', 'disk_gb'] + self.assertEqual(len(result['host']), 3) + for resource in result['host']: + self.assertTrue(resource['resource']['project'] in proj) + self.assertEqual(len(resource['resource']), 5) + self.assertTrue(set(resource['resource'].keys()) == set(column)) db.service_destroy(ctxt, s_ref['id']) def test_show_works_correctly(self): @@ -238,28 +226,13 @@ class HostTestCase(test.TestCase): result = self.controller.show(self.req, s_ref['host']) - c1 = ('resource' in result['host'] and - 'usage' in result['host']) - compute_node = s_ref['compute_node'][0] - c2 = self._dic_is_equal(result['host']['resource'], - compute_node) - c3 = result['host']['usage'].keys() == ['p-01', 'p-02'] - keys = ['vcpus', 'memory_mb'] - c4 = self._dic_is_equal( - result['host']['usage']['p-01'], i_ref1, keys) - disk = i_ref2['root_gb'] + i_ref2['ephemeral_gb'] - if result['host']['usage']['p-01']['local_gb'] == disk: - c6 = True - else: - c6 = False - c5 = self._dic_is_equal( - result['host']['usage']['p-02'], i_ref2, keys) - if result['host']['usage']['p-02']['local_gb'] == disk: - c7 = True - else: - c7 = False - self.assertTrue(c1 and c2 and c3 and c4 and c5 and c6 and c7) - + proj = ['(total)', '(used_now)', '(used_max)', 'p-01', 'p-02'] + column = ['host', 'project', 'cpu', 'memory_mb', 'disk_gb'] + self.assertEqual(len(result['host']), 5) + for resource in result['host']: + self.assertTrue(resource['resource']['project'] in proj) + self.assertEqual(len(resource['resource']), 5) + self.assertTrue(set(resource['resource'].keys()) == set(column)) db.service_destroy(ctxt, s_ref['id']) db.instance_destroy(ctxt, i_ref1['id']) db.instance_destroy(ctxt, i_ref2['id']) diff --git a/nova/tests/api/openstack/compute/test_flavors.py b/nova/tests/api/openstack/compute/test_flavors.py index 8ad5eea0c..465a57497 100644 --- a/nova/tests/api/openstack/compute/test_flavors.py +++ b/nova/tests/api/openstack/compute/test_flavors.py @@ -24,11 +24,15 @@ from nova.api.openstack.compute import flavors from nova.api.openstack import xmlutil import nova.compute.instance_types from nova import exception +from nova import flags from nova import test from nova import utils from nova.tests.api.openstack import fakes +FLAGS = flags.FLAGS + + NS = "{http://docs.openstack.org/compute/api/v1.1}" ATOMNS = "{http://www.w3.org/2005/Atom}" @@ -130,6 +134,34 @@ class FlavorsTest(test.TestCase): } self.assertEqual(flavor, expected) + def test_get_flavor_with_custom_link_prefix(self): + self.flags(osapi_compute_link_prefix='http://zoo.com:42', + osapi_glance_link_prefix='http://circus.com:34') + req = fakes.HTTPRequest.blank('/v2/fake/flavors/1') + flavor = self.controller.show(req, '1') + expected = { + "flavor": { + "id": "1", + "name": "flavor 1", + "ram": "256", + "disk": "10", + "rxtx_factor": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://zoo.com:42/v2/fake/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://zoo.com:42/fake/flavors/1", + }, + ], + }, + } + self.assertEqual(flavor, expected) + def test_get_flavor_list(self): req = fakes.HTTPRequest.blank('/v2/fake/flavors') flavor = self.controller.index(req) diff --git a/nova/tests/api/openstack/compute/test_images.py b/nova/tests/api/openstack/compute/test_images.py index 893574241..460bf437f 100644 --- a/nova/tests/api/openstack/compute/test_images.py +++ b/nova/tests/api/openstack/compute/test_images.py @@ -26,6 +26,7 @@ from lxml import etree import stubout import webob +from nova import flags from nova.api.openstack.compute import images from nova.api.openstack.compute.views import images as images_view from nova.api.openstack import xmlutil @@ -34,6 +35,9 @@ from nova import utils from nova.tests.api.openstack import fakes +FLAGS = flags.FLAGS + + NS = "{http://docs.openstack.org/compute/api/v1.1}" ATOMNS = "{http://www.w3.org/2005/Atom}" NOW_API_FORMAT = "2010-10-11T10:30:22Z" @@ -117,6 +121,60 @@ class ImagesControllerTest(test.TestCase): self.assertDictMatch(expected_image, actual_image) + def test_get_image_with_custom_prefix(self): + self.flags(osapi_compute_link_prefix='http://zoo.com:42', + osapi_glance_link_prefix='http://circus.com:34') + fake_req = fakes.HTTPRequest.blank('/v2/fake/images/123') + actual_image = self.controller.show(fake_req, '124') + href = "http://zoo.com:42/v2/fake/images/124" + bookmark = "http://zoo.com:42/fake/images/124" + alternate = "http://circus.com:34/fake/images/124" + server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" + server_href = "http://localhost/v2/servers/" + server_uuid + server_bookmark = "http://localhost/servers/" + server_uuid + + expected_image = { + "image": { + "id": "124", + "name": "queued snapshot", + "updated": NOW_API_FORMAT, + "created": NOW_API_FORMAT, + "status": "SAVING", + "progress": 25, + "minDisk": 0, + "minRam": 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "metadata": { + "instance_uuid": server_uuid, + "user_id": "fake", + }, + "links": [{ + "rel": "self", + "href": href, + }, + { + "rel": "bookmark", + "href": bookmark, + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate + }], + }, + } + self.assertDictMatch(expected_image, actual_image) + def test_get_image_404(self): fake_req = fakes.HTTPRequest.blank('/v2/fake/images/unknown') self.assertRaises(webob.exc.HTTPNotFound, diff --git a/nova/tests/api/test_auth.py b/nova/tests/api/test_auth.py new file mode 100644 index 000000000..0625957f7 --- /dev/null +++ b/nova/tests/api/test_auth.py @@ -0,0 +1,60 @@ +# Copyright (c) 2012 OpenStack, LLC +# +# 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. + +import webob + +import nova.api.auth +from nova import test + + +class TestNovaKeystoneContextMiddleware(test.TestCase): + + def setUp(self): + + @webob.dec.wsgify() + def fake_app(req): + self.context = req.environ['nova.context'] + return webob.Response() + + self.context = None + self.middleware = nova.api.auth.NovaKeystoneContext(fake_app) + self.request = webob.Request.blank('/') + self.request.headers['X_TENANT_ID'] = 'testtenantid' + self.request.headers['X_AUTH_TOKEN'] = 'testauthtoken' + + def tearDown(self): + pass + + def test_no_user_or_user_id(self): + response = self.request.get_response(self.middleware) + self.assertEqual(response.status, '401 Unauthorized') + + def test_user_only(self): + self.request.headers['X_USER_ID'] = 'testuserid' + response = self.request.get_response(self.middleware) + self.assertEqual(response.status, '200 OK') + self.assertEqual(self.context.user_id, 'testuserid') + + def test_user_id_only(self): + self.request.headers['X_USER'] = 'testuser' + response = self.request.get_response(self.middleware) + self.assertEqual(response.status, '200 OK') + self.assertEqual(self.context.user_id, 'testuser') + + def test_user_id_trumps_user(self): + self.request.headers['X_USER_ID'] = 'testuserid' + self.request.headers['X_USER'] = 'testuser' + response = self.request.get_response(self.middleware) + self.assertEqual(response.status, '200 OK') + self.assertEqual(self.context.user_id, 'testuserid') diff --git a/nova/tests/declare_flags.py b/nova/tests/declare_flags.py index 9dc578867..cb6726a7c 100644 --- a/nova/tests/declare_flags.py +++ b/nova/tests/declare_flags.py @@ -16,8 +16,8 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg FLAGS = flags.FLAGS FLAGS.add_option(cfg.IntOpt('answer', default=42, help='test flag')) diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/fake_libvirt_utils.py index f1d4a8ec7..27b753a64 100644 --- a/nova/tests/fake_libvirt_utils.py +++ b/nova/tests/fake_libvirt_utils.py @@ -21,6 +21,10 @@ disk_sizes = {} disk_backing_files = {} +def get_iscsi_initiator(): + return "fake.initiator.iqn" + + def create_image(disk_format, path, size): pass diff --git a/nova/tests/runtime_flags.py b/nova/tests/runtime_flags.py index 1a28f4fbd..279d51162 100644 --- a/nova/tests/runtime_flags.py +++ b/nova/tests/runtime_flags.py @@ -16,8 +16,8 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg FLAGS = flags.FLAGS FLAGS.add_option(cfg.IntOpt('runtime_answer', default=54, help='test flag')) diff --git a/nova/tests/test_cfg.py b/nova/tests/test_cfg.py deleted file mode 100644 index 6d02134e1..000000000 --- a/nova/tests/test_cfg.py +++ /dev/null @@ -1,789 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Red Hat, 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. - -import os -import sys -import StringIO -import tempfile - -import stubout - -from nova import test -from nova.common.cfg import * - - -class BaseTestCase(test.TestCase): - - def setUp(self): - self.conf = ConfigOpts(prog='test', - version='1.0', - usage='%prog FOO BAR', - default_config_files=[]) - self.tempfiles = [] - self.stubs = stubout.StubOutForTesting() - - def tearDown(self): - self.remove_tempfiles() - self.stubs.UnsetAll() - - def create_tempfiles(self, files): - for (basename, contents) in files: - (fd, path) = tempfile.mkstemp(prefix=basename) - self.tempfiles.append(path) - try: - os.write(fd, contents) - finally: - os.close(fd) - return self.tempfiles[-len(files):] - - def remove_tempfiles(self): - for p in self.tempfiles: - os.remove(p) - - -class LeftoversTestCase(BaseTestCase): - - def test_leftovers(self): - self.conf.register_cli_opt(StrOpt('foo')) - self.conf.register_cli_opt(StrOpt('bar')) - - leftovers = self.conf(['those', '--foo', 'this', - 'thems', '--bar', 'that', 'these']) - - self.assertEquals(leftovers, ['those', 'thems', 'these']) - - -class FindConfigFilesTestCase(BaseTestCase): - - def test_find_config_files(self): - config_files = \ - [os.path.expanduser('~/.blaa/blaa.conf'), '/etc/foo.conf'] - - self.stubs.Set(os.path, 'exists', lambda p: p in config_files) - - self.assertEquals(find_config_files(project='blaa', prog='foo'), - config_files) - - -class CliOptsTestCase(BaseTestCase): - - def _do_cli_test(self, opt_class, default, cli_args, value): - self.conf.register_cli_opt(opt_class('foo', default=default)) - - self.conf(cli_args) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, value) - - def test_str_default(self): - self._do_cli_test(StrOpt, None, [], None) - - def test_str_arg(self): - self._do_cli_test(StrOpt, None, ['--foo', 'bar'], 'bar') - - def test_bool_default(self): - self._do_cli_test(BoolOpt, False, [], False) - - def test_bool_arg(self): - self._do_cli_test(BoolOpt, None, ['--foo'], True) - - def test_bool_arg_inverse(self): - self._do_cli_test(BoolOpt, None, ['--foo', '--nofoo'], False) - - def test_int_default(self): - self._do_cli_test(IntOpt, 10, [], 10) - - def test_int_arg(self): - self._do_cli_test(IntOpt, None, ['--foo=20'], 20) - - def test_float_default(self): - self._do_cli_test(FloatOpt, 1.0, [], 1.0) - - def test_float_arg(self): - self._do_cli_test(FloatOpt, None, ['--foo', '2.0'], 2.0) - - def test_list_default(self): - self._do_cli_test(ListOpt, ['bar'], [], ['bar']) - - def test_list_arg(self): - self._do_cli_test(ListOpt, None, - ['--foo', 'blaa,bar'], ['blaa', 'bar']) - - def test_multistr_default(self): - self._do_cli_test(MultiStrOpt, ['bar'], [], ['bar']) - - def test_multistr_arg(self): - self._do_cli_test(MultiStrOpt, None, - ['--foo', 'blaa', '--foo', 'bar'], ['blaa', 'bar']) - - def test_help(self): - self.stubs.Set(sys, 'stdout', StringIO.StringIO()) - self.assertRaises(SystemExit, self.conf, ['--help']) - self.assertTrue('FOO BAR' in sys.stdout.getvalue()) - self.assertTrue('--version' in sys.stdout.getvalue()) - self.assertTrue('--help' in sys.stdout.getvalue()) - self.assertTrue('--config-file=PATH' in sys.stdout.getvalue()) - - def test_version(self): - self.stubs.Set(sys, 'stdout', StringIO.StringIO()) - self.assertRaises(SystemExit, self.conf, ['--version']) - self.assertTrue('1.0' in sys.stdout.getvalue()) - - def test_config_file(self): - paths = self.create_tempfiles([('1.conf', '[DEFAULT]'), - ('2.conf', '[DEFAULT]')]) - - self.conf(['--config-file', paths[0], '--config-file', paths[1]]) - - self.assertEquals(self.conf.config_file, paths) - - -class ConfigFileOptsTestCase(BaseTestCase): - - def test_conf_file_str_default(self): - self.conf.register_opt(StrOpt('foo', default='bar')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'bar') - - def test_conf_file_str_value(self): - self.conf.register_opt(StrOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'bar') - - def test_conf_file_str_value_override(self): - self.conf.register_cli_opt(StrOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = baar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = baaar\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'baaar') - - def test_conf_file_int_default(self): - self.conf.register_opt(IntOpt('foo', default=666)) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 666) - - def test_conf_file_int_value(self): - self.conf.register_opt(IntOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 666) - - def test_conf_file_int_value_override(self): - self.conf.register_cli_opt(IntOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 66\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--foo', '6', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 666) - - def test_conf_file_float_default(self): - self.conf.register_opt(FloatOpt('foo', default=6.66)) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.66) - - def test_conf_file_float_value(self): - self.conf.register_opt(FloatOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.66) - - def test_conf_file_float_value_override(self): - self.conf.register_cli_opt(FloatOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 6.6\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--foo', '6', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.66) - - def test_conf_file_list_default(self): - self.conf.register_opt(ListOpt('foo', default=['bar'])) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_conf_file_list_value(self): - self.conf.register_opt(ListOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_conf_file_list_value_override(self): - self.conf.register_cli_opt(ListOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = bar,bar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = b,a,r\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['b', 'a', 'r']) - - def test_conf_file_multistr_default(self): - self.conf.register_opt(MultiStrOpt('foo', default=['bar'])) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_conf_file_multistr_value(self): - self.conf.register_opt(MultiStrOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_conf_file_multistr_values_append(self): - self.conf.register_cli_opt(ListOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = bar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - - # FIXME(markmc): values spread across the CLI and multiple - # config files should be appended - # self.assertEquals(self.conf.foo, ['bar', 'bar', 'bar']) - - -class OptGroupsTestCase(BaseTestCase): - - def test_arg_group(self): - blaa_group = OptGroup('blaa') - self.conf.register_group(blaa_group) - self.conf.register_cli_opt(StrOpt('foo'), group=blaa_group) - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_arg_group_by_name(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('foo'), group='blaa') - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_arg_group_with_default(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('foo', default='bar'), group='blaa') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_arg_group_in_config_file(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo'), group='blaa') - - paths = self.create_tempfiles([('test.conf', - '[blaa]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - -class TemplateSubstitutionTestCase(BaseTestCase): - - def _prep_test_str_sub(self, foo_default=None, bar_default=None): - self.conf.register_cli_opt(StrOpt('foo', default=foo_default)) - self.conf.register_cli_opt(StrOpt('bar', default=bar_default)) - - def _assert_str_sub(self): - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEquals(self.conf.bar, 'blaa') - - def test_str_sub_default_from_default(self): - self._prep_test_str_sub(foo_default='blaa', bar_default='$foo') - - self.conf([]) - - self._assert_str_sub() - - def test_str_sub_default_from_arg(self): - self._prep_test_str_sub(bar_default='$foo') - - self.conf(['--foo', 'blaa']) - - self._assert_str_sub() - - def test_str_sub_default_from_config_file(self): - self._prep_test_str_sub(bar_default='$foo') - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_arg_from_default(self): - self._prep_test_str_sub(foo_default='blaa') - - self.conf(['--bar', '$foo']) - - self._assert_str_sub() - - def test_str_sub_arg_from_arg(self): - self._prep_test_str_sub() - - self.conf(['--foo', 'blaa', '--bar', '$foo']) - - self._assert_str_sub() - - def test_str_sub_arg_from_config_file(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0], '--bar=$foo']) - - self._assert_str_sub() - - def test_str_sub_config_file_from_default(self): - self._prep_test_str_sub(foo_default='blaa') - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_config_file_from_arg(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0], '--foo=blaa']) - - self._assert_str_sub() - - def test_str_sub_config_file_from_config_file(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'bar = $foo\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_group_from_default(self): - self.conf.register_cli_opt(StrOpt('foo', default='blaa')) - self.conf.register_group(OptGroup('ba')) - self.conf.register_cli_opt(StrOpt('r', default='$foo'), group='ba') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'ba')) - self.assertTrue(hasattr(self.conf.ba, 'r')) - self.assertEquals(self.conf.ba.r, 'blaa') - - -class ReparseTestCase(BaseTestCase): - - def test_reparse(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('foo', default='r'), group='blaa') - - paths = self.create_tempfiles([('test.conf', - '[blaa]\n' - 'foo = b\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'b') - - self.conf(['--blaa-foo', 'a']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'a') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'r') - - -class OverridesTestCase(BaseTestCase): - - def test_no_default_override(self): - self.conf.register_opt(StrOpt('foo')) - self.conf([]) - self.assertEquals(self.conf.foo, None) - self.conf.set_default('foo', 'bar') - self.assertEquals(self.conf.foo, 'bar') - - def test_default_override(self): - self.conf.register_opt(StrOpt('foo', default='foo')) - self.conf([]) - self.assertEquals(self.conf.foo, 'foo') - self.conf.set_default('foo', 'bar') - self.assertEquals(self.conf.foo, 'bar') - self.conf.set_default('foo', None) - self.assertEquals(self.conf.foo, 'foo') - - def test_override(self): - self.conf.register_opt(StrOpt('foo')) - self.conf.set_override('foo', 'bar') - self.conf([]) - self.assertEquals(self.conf.foo, 'bar') - - def test_group_no_default_override(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo'), group='blaa') - self.conf([]) - self.assertEquals(self.conf.blaa.foo, None) - self.conf.set_default('foo', 'bar', group='blaa') - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_group_default_override(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo', default='foo'), group='blaa') - self.conf([]) - self.assertEquals(self.conf.blaa.foo, 'foo') - self.conf.set_default('foo', 'bar', group='blaa') - self.assertEquals(self.conf.blaa.foo, 'bar') - self.conf.set_default('foo', None, group='blaa') - self.assertEquals(self.conf.blaa.foo, 'foo') - - def test_group_override(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo'), group='blaa') - self.conf.set_override('foo', 'bar', group='blaa') - self.conf([]) - self.assertEquals(self.conf.blaa.foo, 'bar') - - -class SadPathTestCase(BaseTestCase): - - def test_unknown_attr(self): - self.conf([]) - self.assertFalse(hasattr(self.conf, 'foo')) - self.assertRaises(NoSuchOptError, getattr, self.conf, 'foo') - - def test_unknown_attr_is_attr_error(self): - self.conf([]) - self.assertFalse(hasattr(self.conf, 'foo')) - self.assertRaises(AttributeError, getattr, self.conf, 'foo') - - def test_unknown_group_attr(self): - self.conf.register_group(OptGroup('blaa')) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertFalse(hasattr(self.conf.blaa, 'foo')) - self.assertRaises(NoSuchOptError, getattr, self.conf.blaa, 'foo') - - def test_ok_duplicate(self): - opt = StrOpt('foo') - self.conf.register_cli_opt(opt) - self.conf.register_cli_opt(opt) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, None) - - def test_error_duplicate(self): - self.conf.register_cli_opt(StrOpt('foo')) - self.assertRaises(DuplicateOptError, - self.conf.register_cli_opt, StrOpt('foo')) - - def test_error_duplicate_with_different_dest(self): - self.conf.register_cli_opt(StrOpt('foo', dest='f')) - self.assertRaises(DuplicateOptError, - self.conf.register_cli_opt, StrOpt('foo')) - - def test_error_duplicate_short(self): - self.conf.register_cli_opt(StrOpt('foo', short='f')) - self.assertRaises(DuplicateOptError, - self.conf.register_cli_opt, StrOpt('bar', short='f')) - - def test_no_such_group(self): - self.assertRaises(NoSuchGroupError, self.conf.register_cli_opt, - StrOpt('foo'), group='blaa') - - def test_already_parsed(self): - self.conf([]) - - self.assertRaises(ArgsAlreadyParsedError, - self.conf.register_cli_opt, StrOpt('foo')) - - def test_bad_cli_arg(self): - self.stubs.Set(sys, 'stderr', StringIO.StringIO()) - - self.assertRaises(SystemExit, self.conf, ['--foo']) - - self.assertTrue('error' in sys.stderr.getvalue()) - self.assertTrue('--foo' in sys.stderr.getvalue()) - - def _do_test_bad_cli_value(self, opt_class): - self.conf.register_cli_opt(opt_class('foo')) - - self.stubs.Set(sys, 'stderr', StringIO.StringIO()) - - self.assertRaises(SystemExit, self.conf, ['--foo', 'bar']) - - self.assertTrue('foo' in sys.stderr.getvalue()) - self.assertTrue('bar' in sys.stderr.getvalue()) - - def test_bad_int_arg(self): - self._do_test_bad_cli_value(IntOpt) - - def test_bad_float_arg(self): - self._do_test_bad_cli_value(FloatOpt) - - def test_conf_file_not_found(self): - paths = self.create_tempfiles([('test.conf', '')]) - os.remove(paths[0]) - self.tempfiles.remove(paths[0]) - - self.assertRaises(ConfigFilesNotFoundError, - self.conf, ['--config-file', paths[0]]) - - def test_conf_file_broken(self): - paths = self.create_tempfiles([('test.conf', 'foo')]) - - self.assertRaises(ConfigFileParseError, - self.conf, ['--config-file', paths[0]]) - - def _do_test_conf_file_bad_value(self, opt_class): - self.conf.register_opt(opt_class('foo')) - - def test_conf_file_bad_bool(self): - self._do_test_conf_file_bad_value(BoolOpt) - - def test_conf_file_bad_int(self): - self._do_test_conf_file_bad_value(IntOpt) - - def test_conf_file_bad_float(self): - self._do_test_conf_file_bad_value(FloatOpt) - - def test_str_sub_from_group(self): - self.conf.register_group(OptGroup('f')) - self.conf.register_cli_opt(StrOpt('oo', default='blaa'), group='f') - self.conf.register_cli_opt(StrOpt('bar', default='$f.oo')) - - self.conf([]) - - self.assertFalse(hasattr(self.conf, 'bar')) - self.assertRaises(TemplateSubstitutionError, getattr, self.conf, 'bar') - - def test_set_default_unknown_attr(self): - self.conf([]) - self.assertRaises(NoSuchOptError, self.conf.set_default, 'foo', 'bar') - - def test_set_default_unknown_group(self): - self.conf([]) - self.assertRaises(NoSuchGroupError, - self.conf.set_default, 'foo', 'bar', group='blaa') - - def test_set_override_unknown_attr(self): - self.conf([]) - self.assertRaises(NoSuchOptError, self.conf.set_override, 'foo', 'bar') - - def test_set_override_unknown_group(self): - self.conf([]) - self.assertRaises(NoSuchGroupError, - self.conf.set_override, 'foo', 'bar', group='blaa') - - -class OptDumpingTestCase(BaseTestCase): - - class FakeLogger: - - def __init__(self, test_case, expected_lvl): - self.test_case = test_case - self.expected_lvl = expected_lvl - self.logged = [] - - def log(self, lvl, fmt, *args): - self.test_case.assertEquals(lvl, self.expected_lvl) - self.logged.append(fmt % args) - - def test_log_opt_values(self): - self.conf.register_cli_opt(StrOpt('foo')) - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('bar'), 'blaa') - - self.conf(['--foo', 'this', '--blaa-bar', 'that']) - - logger = self.FakeLogger(self, 666) - - self.conf.log_opt_values(logger, 666) - - self.assertEquals(logger.logged, [ - "*" * 80, - "Configuration options gathered from:", - "command line args: ['--foo', 'this', '--blaa-bar', 'that']", - "config files: []", - "=" * 80, - "config_file = []", - "foo = this", - "blaa.bar = that", - "*" * 80, - ]) - - -class CommonOptsTestCase(BaseTestCase): - - def setUp(self): - super(CommonOptsTestCase, self).setUp() - self.conf = CommonConfigOpts() - - def test_debug_verbose(self): - self.conf(['--debug', '--verbose']) - - self.assertEquals(self.conf.debug, True) - self.assertEquals(self.conf.verbose, True) - - def test_logging_opts(self): - self.conf([]) - - self.assertTrue(self.conf.log_config is None) - self.assertTrue(self.conf.log_file is None) - self.assertTrue(self.conf.log_dir is None) - - self.assertEquals(self.conf.log_format, - CommonConfigOpts.DEFAULT_LOG_FORMAT) - self.assertEquals(self.conf.log_date_format, - CommonConfigOpts.DEFAULT_LOG_DATE_FORMAT) - - self.assertEquals(self.conf.use_syslog, False) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 400e6949f..d99aed310 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -190,6 +190,15 @@ class BaseTestCase(test.TestCase): class ComputeTestCase(BaseTestCase): + def setUp(self): + def fake_get_nw_info(cls, ctxt, instance): + self.assertTrue(ctxt.is_admin) + return fake_network.fake_get_instance_nw_info(self.stubs, 1, 1, + spectacular=True) + + super(ComputeTestCase, self).setUp() + self.stubs.Set(nova.network.API, 'get_instance_nw_info', + fake_get_nw_info) def test_wrap_instance_fault(self): inst_uuid = "fake_uuid" @@ -966,38 +975,66 @@ class ComputeTestCase(BaseTestCase): def test_finish_resize(self): """Contrived test to ensure finish_resize doesn't raise anything""" + nw_info = fake_network.fake_get_instance_nw_info(self.stubs, + spectacular=True) + def fake(*args, **kwargs): pass + def fake_nw_info(*args, **kwargs): + return nw_info + + # NOTE(jkoelker) There is a bit of a stubbing issue here. + # fake_network stubs out a bunch of stuff which + # this functional test expects to be acting on + # the db or the stubs it sets. + self.stubs.UnsetAll() + self.stubs.SmartUnsetAll() + self.setUp() + self.stubs.Set(self.compute.driver, 'finish_migration', fake) - self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake) + self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', + fake_nw_info) + fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs, + func=fake_nw_info) context = self.context.elevated() + instance = self._create_fake_instance() self.compute.prep_resize(context, instance['uuid'], 1, filter_properties={}) migration_ref = db.migration_get_by_instance_and_status(context, instance['uuid'], 'pre-migrating') - try: - self.compute.finish_resize(context, instance['uuid'], - int(migration_ref['id']), {}) - except KeyError, e: - # Only catch key errors. We want other reasons for the test to - # fail to actually error out so we don't obscure anything - self.fail() - + self.compute.finish_resize(context, instance['uuid'], + int(migration_ref['id']), {}) self.compute.terminate_instance(self.context, instance['uuid']) def test_finish_resize_handles_error(self): """Make sure we don't leave the instance in RESIZE on error""" + nw_info = fake_network.fake_get_instance_nw_info(self.stubs, + spectacular=True) + def throw_up(*args, **kwargs): raise Exception() def fake(*args, **kwargs): pass + def fake_nw_info(*args, **kwargs): + return nw_info + + # NOTE(jkoelker) There is a bit of a stubbing issue here. + # fake_network stubs out a bunch of stuff which + # this functional test expects to be acting on + # the db or the stubs it sets. + self.stubs.UnsetAll() + self.stubs.SmartUnsetAll() + self.setUp() + self.stubs.Set(self.compute.driver, 'finish_migration', throw_up) self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake) + fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs, + func=fake_nw_info) context = self.context.elevated() instance = self._create_fake_instance() self.compute.prep_resize(context, instance['uuid'], 1, @@ -1123,13 +1160,32 @@ class ComputeTestCase(BaseTestCase): def test_finish_revert_resize(self): """Ensure that the flavor is reverted to the original on revert""" - context = self.context.elevated() - instance = self._create_fake_instance() - instance_uuid = instance['uuid'] + nw_info = fake_network.fake_get_instance_nw_info(self.stubs, + spectacular=True) def fake(*args, **kwargs): pass + def fake_nw_info(*args, **kwargs): + return nw_info + + # NOTE(jkoelker) There is a bit of a stubbing issue here. + # fake_network stubs out a bunch of stuff which + # this functional test expects to be acting on + # the db or the stubs it sets. + self.stubs.UnsetAll() + self.stubs.SmartUnsetAll() + self.setUp() + + self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', + fake_nw_info) + fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs, + func=fake_nw_info) + + context = self.context.elevated() + instance = self._create_fake_instance() + instance_uuid = instance['uuid'] + self.stubs.Set(self.compute.driver, 'finish_migration', fake) self.stubs.Set(self.compute.driver, 'finish_revert_migration', fake) self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake) @@ -1450,7 +1506,14 @@ class ComputeTestCase(BaseTestCase): class ComputeAPITestCase(BaseTestCase): def setUp(self): + def fake_get_nw_info(cls, ctxt, instance): + self.assertTrue(ctxt.is_admin) + return fake_network.fake_get_instance_nw_info(self.stubs, 1, 1, + spectacular=True) + super(ComputeAPITestCase, self).setUp() + self.stubs.Set(nova.network.API, 'get_instance_nw_info', + fake_get_nw_info) self.compute_api = compute.API() self.fake_image = { 'id': 1, @@ -2267,17 +2330,9 @@ class ComputeAPITestCase(BaseTestCase): fixed_address): called['associate'] = True - def fake_get_nw_info(cls, ctxt, instance): - self.assertTrue(ctxt.is_admin) - return fake_network.fake_get_instance_nw_info(self.stubs, 1, 1, - spectacular=True) - self.stubs.Set(nova.network.API, 'associate_floating_ip', fake_associate_ip_network_api) - self.stubs.Set(nova.network.API, 'get_instance_nw_info', - fake_get_nw_info) - instance = self._create_fake_instance() instance_uuid = instance['uuid'] address = '0.1.2.3' @@ -2995,12 +3050,6 @@ class ComputeAPITestCase(BaseTestCase): self.assertTrue(self.compute_api.get_lock(self.context, instance)) def test_add_remove_security_group(self): - def fake_get_nw_info(cls, ctxt, instance): - return fake_network.fake_get_instance_nw_info(self.stubs, 1, 1, - spectacular=True) - - self.stubs.Set(nova.network.API, 'get_instance_nw_info', - fake_get_nw_info) instance = self._create_fake_instance() self.compute.run_instance(self.context, instance['uuid']) diff --git a/nova/tests/test_compute_utils.py b/nova/tests/test_compute_utils.py index 71463fbc0..8e35d1905 100644 --- a/nova/tests/test_compute_utils.py +++ b/nova/tests/test_compute_utils.py @@ -29,6 +29,7 @@ import nova.image.fake from nova.compute import utils as compute_utils from nova.compute import instance_types from nova.notifier import test_notifier +from nova.tests import fake_network LOG = logging.getLogger('nova.tests.compute_utils') @@ -39,7 +40,15 @@ flags.DECLARE('stub_network', 'nova.compute.manager') class UsageInfoTestCase(test.TestCase): def setUp(self): + def fake_get_nw_info(cls, ctxt, instance): + self.assertTrue(ctxt.is_admin) + return fake_network.fake_get_instance_nw_info(self.stubs, 1, 1, + spectacular=True) + super(UsageInfoTestCase, self).setUp() + self.stubs.Set(nova.network.API, 'get_instance_nw_info', + fake_get_nw_info) + self.flags(connection_type='fake', stub_network=True, notification_driver='nova.notifier.test_notifier', diff --git a/nova/tests/test_flags.py b/nova/tests/test_flags.py index 02ee49ef0..bea34007a 100644 --- a/nova/tests/test_flags.py +++ b/nova/tests/test_flags.py @@ -21,8 +21,8 @@ import exceptions import os import tempfile -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg from nova import test FLAGS = flags.FLAGS diff --git a/nova/tests/test_imagecache.py b/nova/tests/test_imagecache.py new file mode 100644 index 000000000..dee937bd2 --- /dev/null +++ b/nova/tests/test_imagecache.py @@ -0,0 +1,243 @@ +#!/usr/bin/python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Michael Still and Canonical Inc +# 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. + + +import hashlib +import os +import shutil +import tempfile +import time + +from nova import test + +from nova import db +from nova import flags +from nova import log as logging +from nova.virt.libvirt import imagecache +from nova.virt.libvirt import utils as virtutils + + +flags.DECLARE('instances_path', 'nova.compute.manager') +FLAGS = flags.FLAGS + +LOG = logging.getLogger('nova.tests.test_imagecache') + + +class ImageCacheManagerTestCase(test.TestCase): + + def test_read_stored_checksum_missing(self): + self.stubs.Set(os.path, 'exists', lambda x: False) + + csum = imagecache.read_stored_checksum('/tmp/foo') + self.assertEquals(csum, None) + + def test_read_stored_checksum(self): + try: + dirname = tempfile.mkdtemp() + fname = os.path.join(dirname, 'aaa') + + csum_input = 'fdghkfhkgjjksfdgjksjkghsdf' + f = open('%s.sha1' % fname, 'w') + f.write('%s\n' % csum_input) + f.close() + + csum_output = imagecache.read_stored_checksum(fname) + + self.assertEquals(csum_input, csum_output) + + finally: + shutil.rmtree(dirname) + + def test_list_base_images(self): + listing = ['00000001', + 'ephemeral_0_20_None', + 'e97222e91fc4241f49a7f520d1dcf446751129b3_sm', + 'e09c675c2d1cfac32dae3c2d83689c8c94bc693b_sm', + 'e97222e91fc4241f49a7f520d1dcf446751129b3', + '00000004'] + + self.stubs.Set(os, 'listdir', lambda x: listing) + self.stubs.Set(os.path, 'isfile', lambda x: True) + + base_dir = '/var/lib/nova/instances/_base' + image_cache_manager = imagecache.ImageCacheManager() + image_cache_manager._list_base_images(base_dir) + + self.assertEquals(len(image_cache_manager.unexplained_images), 3) + + expected = os.path.join(base_dir, + 'e97222e91fc4241f49a7f520d1dcf446751129b3') + self.assertTrue(expected in image_cache_manager.unexplained_images) + + unexpected = os.path.join(base_dir, '00000004') + self.assertFalse(unexpected in image_cache_manager.unexplained_images) + + for ent in image_cache_manager.unexplained_images: + self.assertTrue(ent.startswith(base_dir)) + + def test_list_running_instances(self): + self.stubs.Set(db, 'instance_get_all', + lambda x: [{'image_ref': 'image-1', + 'host': FLAGS.host, + 'name': 'inst-1'}, + {'image_ref': 'image-2', + 'host': FLAGS.host, + 'name': 'inst-2'}, + {'image_ref': 'image-2', + 'host': 'remotehost', + 'name': 'inst-3'}]) + + image_cache_manager = imagecache.ImageCacheManager() + + # The argument here should be a context, but its mocked out + image_cache_manager._list_running_instances(None) + + self.assertEqual(len(image_cache_manager.used_images), 2) + self.assertTrue(image_cache_manager.used_images['image-1'] == + (1, 0, ['inst-1'])) + self.assertTrue(image_cache_manager.used_images['image-2'] == + (1, 1, ['inst-2', 'inst-3'])) + + self.assertEqual(len(image_cache_manager.image_popularity), 2) + self.assertEqual(image_cache_manager.image_popularity['image-1'], 1) + self.assertEqual(image_cache_manager.image_popularity['image-2'], 2) + + def test_list_backing_images(self): + self.stubs.Set(os, 'listdir', + lambda x: ['_base', 'instance-00000001', + 'instance-00000002', 'instance-00000003']) + self.stubs.Set(os.path, 'exists', + lambda x: x.find('instance-') != -1) + self.stubs.Set(virtutils, 'get_disk_backing_file', + lambda x: 'e97222e91fc4241f49a7f520d1dcf446751129b3_sm') + + found = os.path.join(FLAGS.instances_path, '_base', + 'e97222e91fc4241f49a7f520d1dcf446751129b3_sm') + + image_cache_manager = imagecache.ImageCacheManager() + image_cache_manager.unexplained_images = [found] + + inuse_images = image_cache_manager._list_backing_images() + + self.assertEquals(inuse_images, [found]) + self.assertEquals(len(image_cache_manager.unexplained_images), 0) + + def test_find_base_file_nothing(self): + self.stubs.Set(os.path, 'exists', lambda x: False) + + base_dir = '/var/lib/nova/instances/_base' + fingerprint = '549867354867' + image_cache_manager = imagecache.ImageCacheManager() + res = list(image_cache_manager._find_base_file(base_dir, fingerprint)) + + self.assertEqual(0, len(res)) + + def test_find_base_file_small(self): + self.stubs.Set(os.path, 'exists', + lambda x: x.endswith('549867354867_sm')) + + base_dir = '/var/lib/nova/instances/_base' + fingerprint = '549867354867' + image_cache_manager = imagecache.ImageCacheManager() + res = list(image_cache_manager._find_base_file(base_dir, fingerprint)) + + base_file = os.path.join(base_dir, fingerprint + '_sm') + self.assertTrue(res == [(base_file, True)]) + + def test_find_base_file_both(self): + self.stubs.Set(os.path, 'exists', lambda x: True) + + base_dir = '/var/lib/nova/instances/_base' + fingerprint = '549867354867' + image_cache_manager = imagecache.ImageCacheManager() + res = list(image_cache_manager._find_base_file(base_dir, fingerprint)) + + base_file1 = os.path.join(base_dir, fingerprint) + base_file2 = os.path.join(base_dir, fingerprint + '_sm') + self.assertTrue(res == [(base_file1, False), (base_file2, True)]) + + def test_verify_checksum(self): + testdata = ('OpenStack Software delivers a massively scalable cloud ' + 'operating system.') + img = {'container_format': 'ami', 'id': '42'} + + try: + dirname = tempfile.mkdtemp() + fname = os.path.join(dirname, 'aaa') + + f = open(fname, 'w') + f.write(testdata) + f.close() + + # Checksum is valid + f = open('%s.sha1' % fname, 'w') + csum = hashlib.sha1() + csum.update(testdata) + f.write(csum.hexdigest()) + f.close() + + image_cache_manager = imagecache.ImageCacheManager() + res = image_cache_manager._verify_checksum(img, fname) + self.assertTrue(res) + + # Checksum is invalid + f = open('%s.sha1' % fname, 'w') + f.write('banana') + f.close() + + image_cache_manager = imagecache.ImageCacheManager() + res = image_cache_manager._verify_checksum(img, fname) + self.assertFalse(res) + + # Checksum file missing + os.remove('%s.sha1' % fname) + image_cache_manager = imagecache.ImageCacheManager() + res = image_cache_manager._verify_checksum(img, fname) + self.assertEquals(res, None) + + finally: + shutil.rmtree(dirname) + + def test_remove_base_file(self): + try: + dirname = tempfile.mkdtemp() + fname = os.path.join(dirname, 'aaa') + + f = open(fname, 'w') + f.write('data') + f.close() + + f = open('%s.sha1' % fname, 'w') + f.close() + + image_cache_manager = imagecache.ImageCacheManager() + image_cache_manager._remove_base_file(fname) + + # Files are initially too new to delete + self.assertTrue(os.path.exists(fname)) + self.assertTrue(os.path.exists('%s.sha1' % fname)) + + # Old files get cleaned up though + os.utime(fname, (-1, time.time() - 100000)) + image_cache_manager._remove_base_file(fname) + + self.assertFalse(os.path.exists(fname)) + self.assertFalse(os.path.exists('%s.sha1' % fname)) + + finally: + shutil.rmtree(dirname) diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index b0613c757..29b812a2b 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -174,7 +174,7 @@ class InstanceTypeTestCase(test.TestCase): def test_will_not_get_bad_default_instance_type(self): """ensures error raised on bad default instance type""" FLAGS.default_instance_type = 'unknown_flavor' - self.assertRaises(exception.InstanceTypeNotFoundByName, + self.assertRaises(exception.ApiError, instance_types.get_default_instance_type) def test_will_get_instance_type_by_id(self): @@ -185,12 +185,12 @@ class InstanceTypeTestCase(test.TestCase): def test_will_not_get_instance_type_by_unknown_id(self): """Ensure get by name returns default flavor with no name""" - self.assertRaises(exception.InstanceTypeNotFound, + self.assertRaises(exception.ApiError, instance_types.get_instance_type, 10000) def test_will_not_get_instance_type_with_bad_id(self): """Ensure get by name returns default flavor with bad name""" - self.assertRaises(exception.InstanceTypeNotFound, + self.assertRaises(exception.ApiError, instance_types.get_instance_type, 'asdf') def test_instance_type_get_by_None_name_returns_default(self): @@ -201,7 +201,7 @@ class InstanceTypeTestCase(test.TestCase): def test_will_not_get_instance_type_with_bad_name(self): """Ensure get by name returns default flavor with bad name""" - self.assertRaises(exception.InstanceTypeNotFoundByName, + self.assertRaises(exception.ApiError, instance_types.get_instance_type_by_name, 10000) def test_will_not_get_instance_by_unknown_flavor_id(self): diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index cbe45ed2f..72c9f8802 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -123,6 +123,10 @@ class LibvirtVolumeTestCase(test.TestCase): def get_hypervisor_type(self): return self.hyperv self.fake_conn = FakeLibvirtConnection("Xen") + self.connr = { + 'ip': '127.0.0.1', + 'initiator': 'fake_initiator' + } def test_libvirt_iscsi_driver(self): # NOTE(vish) exists is to make driver assume connecting worked @@ -136,8 +140,7 @@ class LibvirtVolumeTestCase(test.TestCase): 'name': name, 'provider_auth': None, 'provider_location': '%s,fake %s' % (location, iqn)} - address = '127.0.0.1' - connection_info = vol_driver.initialize_connection(vol, '127.0.0.1') + connection_info = vol_driver.initialize_connection(vol, self.connr) mount_device = "vde" xml = libvirt_driver.connect_volume(connection_info, mount_device) tree = xml_to_tree(xml) @@ -145,7 +148,7 @@ class LibvirtVolumeTestCase(test.TestCase): self.assertEqual(tree.get('type'), 'block') self.assertEqual(tree.find('./source').get('dev'), dev_str) libvirt_driver.disconnect_volume(connection_info, mount_device) - connection_info = vol_driver.terminate_connection(vol, '127.0.0.1') + connection_info = vol_driver.terminate_connection(vol, self.connr) expected_commands = [('iscsiadm', '-m', 'node', '-T', iqn, '-p', location), ('iscsiadm', '-m', 'node', '-T', iqn, @@ -167,8 +170,7 @@ class LibvirtVolumeTestCase(test.TestCase): libvirt_driver = volume.LibvirtNetVolumeDriver(self.fake_conn) name = 'volume-00000001' vol = {'id': 1, 'name': name} - address = '127.0.0.1' - connection_info = vol_driver.initialize_connection(vol, address) + connection_info = vol_driver.initialize_connection(vol, self.connr) mount_device = "vde" xml = libvirt_driver.connect_volume(connection_info, mount_device) tree = xml_to_tree(xml) @@ -176,15 +178,14 @@ class LibvirtVolumeTestCase(test.TestCase): self.assertEqual(tree.find('./source').get('protocol'), 'sheepdog') self.assertEqual(tree.find('./source').get('name'), name) libvirt_driver.disconnect_volume(connection_info, mount_device) - connection_info = vol_driver.terminate_connection(vol, '127.0.0.1') + connection_info = vol_driver.terminate_connection(vol, self.connr) def test_libvirt_rbd_driver(self): vol_driver = volume_driver.RBDDriver() libvirt_driver = volume.LibvirtNetVolumeDriver(self.fake_conn) name = 'volume-00000001' vol = {'id': 1, 'name': name} - address = '127.0.0.1' - connection_info = vol_driver.initialize_connection(vol, address) + connection_info = vol_driver.initialize_connection(vol, self.connr) mount_device = "vde" xml = libvirt_driver.connect_volume(connection_info, mount_device) tree = xml_to_tree(xml) @@ -193,7 +194,7 @@ class LibvirtVolumeTestCase(test.TestCase): rbd_name = '%s/%s' % (FLAGS.rbd_pool, name) self.assertEqual(tree.find('./source').get('name'), rbd_name) libvirt_driver.disconnect_volume(connection_info, mount_device) - connection_info = vol_driver.terminate_connection(vol, '127.0.0.1') + connection_info = vol_driver.terminate_connection(vol, self.connr) class CacheConcurrencyTestCase(test.TestCase): @@ -355,6 +356,22 @@ class LibvirtConnTestCase(test.TestCase): return db.service_create(context.get_admin_context(), service_ref) + def test_get_connector(self): + initiator = 'fake.initiator.iqn' + ip = 'fakeip' + self.flags(my_ip=ip) + + conn = connection.LibvirtConnection(True) + expected = { + 'ip': ip, + 'initiator': initiator + } + volume = { + 'id': 'fake' + } + result = conn.get_volume_connector(volume) + self.assertDictMatch(expected, result) + def test_preparing_xml_info(self): conn = connection.LibvirtConnection(True) instance_ref = db.instance_create(self.context, self.test_instance) @@ -1764,6 +1781,17 @@ class NWFilterTestCase(test.TestCase): class LibvirtUtilsTestCase(test.TestCase): + def test_get_iscsi_initiator(self): + self.mox.StubOutWithMock(utils, 'execute') + initiator = 'fake.initiator.iqn' + rval = ("junk\nInitiatorName=%s\njunk\n" % initiator, None) + utils.execute('cat', '/etc/iscsi/initiatorname.iscsi', + run_as_root=True).AndReturn(rval) + # Start test + self.mox.ReplayAll() + result = libvirt_utils.get_iscsi_initiator() + self.assertEqual(initiator, result) + def test_create_image(self): self.mox.StubOutWithMock(utils, 'execute') utils.execute('qemu-img', 'create', '-f', 'raw', diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index 21c5284a5..f81bb0cbc 100644 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -432,6 +432,31 @@ class LinuxNetworkTestCase(test.TestCase): driver.plug({"bridge": "br100", "bridge_interface": "eth0"}, "fakemac") + def test_vlan_override(self): + """Makes sure vlan_interface flag overrides network bridge_interface. + + Allows heterogeneous networks a la bug 833426""" + + driver = linux_net.LinuxBridgeInterfaceDriver() + + @classmethod + def test_ensure(_self, vlan, bridge, interface, network, mac_address): + self.passed_interface = interface + + self.stubs.Set(linux_net.LinuxBridgeInterfaceDriver, + 'ensure_vlan_bridge', test_ensure) + + network = { + "bridge": "br100", + "bridge_interface": "base_interface", + "vlan": "fake" + } + driver.plug(network, "fakemac") + self.assertEqual(self.passed_interface, "base_interface") + self.flags(vlan_interface="override_interface") + driver.plug(network, "fakemac") + self.assertEqual(self.passed_interface, "override_interface") + def _test_initialize_gateway(self, existing, expected, routes=''): self.flags(fake_network=False) executes = [] diff --git a/nova/tests/test_notifier.py b/nova/tests/test_notifier.py index 00f367f49..b13f203a4 100644 --- a/nova/tests/test_notifier.py +++ b/nova/tests/test_notifier.py @@ -16,6 +16,7 @@ import stubout import nova +import nova.notifier.no_op_notifier from nova import log import nova.notifier.api from nova.notifier.api import notify @@ -26,6 +27,7 @@ class NotifierTestCase(test.TestCase): """Test case for notifications""" def setUp(self): super(NotifierTestCase, self).setUp() + self.flags(notification_driver='nova.notifier.no_op_notifier') self.stubs = stubout.StubOutForTesting() def tearDown(self): diff --git a/nova/tests/test_nova_rootwrap.py b/nova/tests/test_nova_rootwrap.py index 4dc476615..38cce3b35 100644 --- a/nova/tests/test_nova_rootwrap.py +++ b/nova/tests/test_nova_rootwrap.py @@ -93,6 +93,15 @@ class RootwrapTestCase(test.TestCase): # Providing -9 signal should work self.assertTrue(f.match(usercmd)) + def test_ReadFileFilter(self): + goodfn = '/good/file.name' + f = filters.ReadFileFilter(goodfn) + usercmd = ['cat', '/bad/file'] + self.assertFalse(f.match(['cat', '/bad/file'])) + usercmd = ['cat', goodfn] + self.assertEqual(f.get_command(usercmd), ['/bin/cat', goodfn]) + self.assertTrue(f.match(usercmd)) + def test_skips(self): # Check that all filters are skipped and that the last matches usercmd = ["cat", "/"] diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 3c267b8b0..23660333a 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -46,7 +46,8 @@ class QuotaTestCase(test.TestCase): quota_cores=4, quota_volumes=2, quota_gigabytes=20, - quota_floating_ips=1) + quota_floating_ips=1, + network_manager='nova.network.manager.FlatDHCPManager') self.network = self.network = self.start_service('network') self.user_id = 'admin' @@ -97,9 +98,19 @@ class QuotaTestCase(test.TestCase): dict(memory_mb=4096, vcpus=2, root_gb=40, flavorid=3), 'm1.large': dict(memory_mb=8192, vcpus=4, root_gb=80, flavorid=4), 'm1.xlarge': - dict(memory_mb=16384, vcpus=8, root_gb=160, flavorid=5)} + dict(memory_mb=16384, vcpus=8, root_gb=160, flavorid=5), + 'm1.nocpu': dict(memory_mb=512, vcpus=0, root_gb=0, flavorid=6), + 'm1.nomem': dict(memory_mb=0, vcpus=1, root_gb=0, flavorid=7)} return instance_types[name] + def test_quota_no_mem_no_cpu(self): + num_instances = quota.allowed_instances(self.context, 100, + self._get_instance_type('m1.nocpu')) + self.assertEqual(num_instances, 2) + num_instances = quota.allowed_instances(self.context, 100, + self._get_instance_type('m1.nomem')) + self.assertEqual(num_instances, 2) + def test_quota_overrides(self): """Make sure overriding a projects quotas works""" num_instances = quota.allowed_instances(self.context, 100, diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py index c8a18f7d8..edede55b3 100644 --- a/nova/tests/test_service.py +++ b/nova/tests/test_service.py @@ -22,11 +22,11 @@ Unit Tests for remote procedure calls using queue import mox -from nova.common import cfg from nova import context from nova import db from nova import exception from nova import flags +from nova.openstack.common import cfg from nova import test from nova import service from nova import manager diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index a99356acb..5da717bee 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -17,7 +17,9 @@ import __builtin__ import mox import datetime +import hashlib import os +import StringIO import tempfile import nova @@ -380,6 +382,18 @@ class GenericUtilsTestCase(test.TestCase): self.assertTrue([c for c in password if c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']) + def test_read_file_as_root(self): + def fake_execute(*args, **kwargs): + if args[1] == 'bad': + raise exception.ProcessExecutionError + return 'fakecontents', None + + self.stubs.Set(utils, 'execute', fake_execute) + contents = utils.read_file_as_root('good') + self.assertEqual(contents, 'fakecontents') + self.assertRaises(exception.FileNotFound, + utils.read_file_as_root, 'bad') + class IsUUIDLikeTestCase(test.TestCase): def assertUUIDLike(self, val, expected): @@ -679,3 +693,10 @@ class DeprecationTest(test.TestCase): self.mox.ReplayAll() result = utils.service_is_up(service) self.assertFalse(result) + + def test_hash_file(self): + data = 'Mary had a little lamb, its fleece as white as snow' + flo = StringIO.StringIO(data) + h1 = utils.hash_file(flo) + h2 = hashlib.sha1(data).hexdigest() + self.assertEquals(h1, h2) diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py index adf8f8eb8..2cda7a61e 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -225,6 +225,12 @@ class _VirtDriverTestCase(test.TestCase): self.connection.list_instances()) @catch_notimplementederror + def test_get_volume_connector(self): + result = self.connection.get_volume_connector({'id': 'fake'}) + self.assertTrue('ip' in result) + self.assertTrue('initiator' in result) + + @catch_notimplementederror def test_attach_detach_volume(self): instance_ref, network_info = self._get_running_instance() self.connection.attach_volume({'driver_volume_type': 'fake'}, @@ -444,13 +450,14 @@ class LibvirtConnTestCase(_VirtDriverTestCase): # Point _VirtDriverTestCase at the right module self.driver_module = nova.virt.libvirt.connection + FLAGS.firewall_driver = nova.virt.libvirt.firewall.drivers[0] super(LibvirtConnTestCase, self).setUp() FLAGS.rescue_image_id = "2" FLAGS.rescue_kernel_id = "3" FLAGS.rescue_ramdisk_id = None def tearDown(self): - super(LibvirtConnTestCase, self).setUp() + super(LibvirtConnTestCase, self).tearDown() # Restore libvirt import nova.virt.libvirt.connection diff --git a/nova/utils.py b/nova/utils.py index d1968ac4d..a98897d82 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -22,6 +22,7 @@ import contextlib import datetime import functools +import hashlib import inspect import json import lockfile @@ -45,10 +46,10 @@ from eventlet import semaphore from eventlet.green import subprocess import netaddr -from nova.common import cfg from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg LOG = logging.getLogger("nova.utils") @@ -324,7 +325,7 @@ def default_flagfile(filename='nova.conf', args=None): args = sys.argv for arg in args: if arg.find('flagfile') != -1: - break + return arg[arg.index('flagfile') + 1:] else: if not os.path.isabs(filename): # turn relative filename into an absolute path @@ -337,6 +338,7 @@ def default_flagfile(filename='nova.conf', args=None): if os.path.exists(filename): flagfile = '--flagfile=%s' % filename args.insert(1, flagfile) + return filename def debug(arg): @@ -1194,6 +1196,13 @@ def read_cached_file(filename, cache_info, reload_func=None): return cache_info['data'] +def hash_file(file_like_object): + """Generate a hash for the contents of a file.""" + checksum = hashlib.sha1() + any(map(checksum.update, iter(lambda: file_like_object.read(32768), ''))) + return checksum.hexdigest() + + @contextlib.contextmanager def temporary_mutation(obj, **kwargs): """Temporarily set the attr on a particular object to a given value then @@ -1404,3 +1413,12 @@ def generate_mac_address(): random.randint(0x00, 0xff), random.randint(0x00, 0xff)] return ':'.join(map(lambda x: "%02x" % x, mac)) + + +def read_file_as_root(file_path): + """Secure helper to read file as root.""" + try: + out, _err = execute('cat', file_path, run_as_root=True) + return out + except exception.ProcessExecutionError: + raise exception.FileNotFound(file_path=file_path) diff --git a/nova/virt/connection.py b/nova/virt/connection.py index ad9998fa2..44bed9b4c 100644 --- a/nova/virt/connection.py +++ b/nova/virt/connection.py @@ -56,6 +56,7 @@ def get_connection(read_only=False): * fake * libvirt * xenapi + * vmwareapi """ # TODO(termie): maybe lazy load after initial check for permissions # TODO(termie): check whether we can be disconnected diff --git a/nova/virt/disk/api.py b/nova/virt/disk/api.py index 223f08a2f..929e17a55 100644 --- a/nova/virt/disk/api.py +++ b/nova/virt/disk/api.py @@ -29,10 +29,10 @@ import json import os import tempfile -from nova.common import cfg from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.virt.disk import guestfs from nova.virt.disk import loop diff --git a/nova/virt/disk/nbd.py b/nova/virt/disk/nbd.py index 19904d694..d43fd3250 100644 --- a/nova/virt/disk/nbd.py +++ b/nova/virt/disk/nbd.py @@ -18,8 +18,8 @@ import os import time -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg from nova import utils from nova.virt.disk import mount diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 84037e6dd..d87a7286a 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -612,3 +612,28 @@ class ComputeDriver(object): """ # TODO(tr3buchet): update all subclasses and remove this return True + + def manage_image_cache(self, context): + """ + Manage the driver's local image cache. + + Some drivers chose to cache images for instances on disk. This method + is an opportunity to do management of that cache which isn't directly + related to other calls into the driver. The prime example is to clean + the cache and remove images which are no longer of interest. + """ + raise NotImplementedError() + + def get_volume_connector(self, instance): + """ + Get connector information for the instance for attaching to volumes. + + Connector information is a dictionary representing the ip of the + machine that will be making the connection and and the name of the + iscsi initiator as follows: + { + 'ip': ip, + 'initiator': initiator, + } + """ + raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 780d644eb..ff8da4724 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -301,3 +301,6 @@ class FakeConnection(driver.ComputeDriver): def get_disk_available_least(self): """ """ pass + + def get_volume_connector(self, instance): + return {'ip': '127.0.0.1', 'initiator': 'fake'} diff --git a/nova/virt/firewall.py b/nova/virt/firewall.py index 2af28d7f1..2996147c7 100644 --- a/nova/virt/firewall.py +++ b/nova/virt/firewall.py @@ -17,11 +17,11 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.common import cfg from nova import context from nova import db from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.virt import netutils @@ -135,7 +135,7 @@ class IptablesFirewallDriver(FirewallDriver): self.instances[instance['id']] = instance self.network_infos[instance['id']] = network_info self.add_filters_for_instance(instance) - LOG.debug(_('Filters added to the instance: %r'), instance) + LOG.debug(_('Filters added to instance %s'), instance['uuid']) self.refresh_provider_fw_rules() LOG.debug(_('Provider Firewall Rules refreshed')) self.iptables.apply() diff --git a/nova/virt/interfaces.template b/nova/virt/interfaces.template index ad8c1bcdb..e75c37357 100644 --- a/nova/virt/interfaces.template +++ b/nova/virt/interfaces.template @@ -14,7 +14,9 @@ iface ${ifc.name} inet static netmask ${ifc.netmask} broadcast ${ifc.broadcast} gateway ${ifc.gateway} +#if $ifc.dns dns-nameservers ${ifc.dns} +#end if #if $use_ipv6 iface ${ifc.name} inet6 static diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 4455dacec..37ad07736 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -54,7 +54,6 @@ from xml.etree import ElementTree from nova.auth import manager from nova import block_device -from nova.common import cfg from nova.compute import instance_types from nova.compute import power_state from nova import context as nova_context @@ -63,10 +62,13 @@ from nova import exception from nova import flags import nova.image from nova import log as logging +from nova.openstack.common import cfg from nova import utils -from nova.virt.disk import api as disk from nova.virt import driver from nova.virt import images +from nova.virt.disk import api as disk +from nova.virt.libvirt import firewall +from nova.virt.libvirt import imagecache from nova.virt.libvirt import utils as libvirt_utils @@ -186,10 +188,12 @@ class LibvirtConnection(driver.ComputeDriver): super(LibvirtConnection, self).__init__() self._host_state = None + self._initiator = None self._wrapped_conn = None self.container = None self.read_only = read_only - + if FLAGS.firewall_driver not in firewall.drivers: + FLAGS['firewall_driver'].SetDefault(firewall.drivers[0]) fw_class = utils.import_class(FLAGS.firewall_driver) self.firewall_driver = fw_class(get_connection=self._get_connection) self.vif_driver = utils.import_object(FLAGS.libvirt_vif_driver) @@ -209,6 +213,8 @@ class LibvirtConnection(driver.ComputeDriver): self.default_ephemeral_device = self._disk_prefix + 'b' self.default_swap_device = self._disk_prefix + 'c' + self.image_cache_manager = imagecache.ImageCacheManager() + @property def host_state(self): if not self._host_state: @@ -420,6 +426,16 @@ class LibvirtConnection(driver.ComputeDriver): if os.path.exists(target): shutil.rmtree(target) + def get_volume_connector(self, _instance): + if not self._initiator: + self._initiator = libvirt_utils.get_iscsi_initiator() + if not self._initiator: + LOG.warn(_('Could not determine iscsi initiator name')) + return { + 'ip': FLAGS.my_ip, + 'initiator': self._initiator, + } + def volume_driver_method(self, method_name, connection_info, *args, **kwargs): driver_type = connection_info.get('driver_volume_type') @@ -690,8 +706,7 @@ class LibvirtConnection(driver.ComputeDriver): block_device_info=block_device_info) domain = self._create_new_domain(xml) - LOG.debug(_("instance %s: is running"), instance['name'], - instance=instance) + LOG.debug(_("instance is running"), instance=instance) self.firewall_driver.apply_instance_filter(instance, network_info) def _wait_for_boot(): @@ -701,13 +716,13 @@ class LibvirtConnection(driver.ComputeDriver): try: state = self.get_info(instance_name)['state'] except exception.NotFound: - msg = _("During reboot, %s disappeared.") % instance_name - LOG.error(msg, instance=instance) + LOG.error(_("During reboot, instance disappeared."), + instance=instance) raise utils.LoopingCallDone if state == power_state.RUNNING: - msg = _("Instance %s spawned successfully.") % instance_name - LOG.info(msg, instance=instance) + LOG.info(_("Instance spawned successfully."), + instance=instance) raise utils.LoopingCallDone timer = utils.LoopingCall(_wait_for_boot) @@ -720,7 +735,7 @@ class LibvirtConnection(driver.ComputeDriver): if virsh_output.startswith('/dev/'): LOG.info(_("cool, it's a device")) out, err = utils.execute('dd', - "if=%s" % virsh_output, + 'if=%s' % virsh_output, 'iflag=nonblock', run_as_root=True, check_exit_code=False) @@ -734,10 +749,11 @@ class LibvirtConnection(driver.ComputeDriver): fp.write(data) return fpath - def _inject_files(self, instance, files): + def _inject_files(self, instance, files, partition): disk_path = os.path.join(FLAGS.instances_path, instance['name'], 'disk') - disk.inject_files(disk_path, files, use_cow=FLAGS.use_cow_images) + disk.inject_files(disk_path, files, partition=partition, + use_cow=FLAGS.use_cow_images) @exception.wrap_exception() def get_console_output(self, instance): @@ -748,7 +764,8 @@ class LibvirtConnection(driver.ComputeDriver): if FLAGS.libvirt_type == 'xen': # Xen is special - virsh_output = utils.execute('virsh', 'ttyconsole', + virsh_output = utils.execute('virsh', + 'ttyconsole', instance['name']) data = self._flush_xen_console(virsh_output) fpath = self._append_to_file(data, console_log) @@ -823,7 +840,6 @@ class LibvirtConnection(driver.ComputeDriver): """ generating = 'image_id' not in kwargs - if not os.path.exists(target): base_dir = os.path.join(FLAGS.instances_path, '_base') if not os.path.exists(base_dir): @@ -866,6 +882,16 @@ class LibvirtConnection(driver.ComputeDriver): """Grab image to raw format""" images.fetch_to_raw(context, image_id, target, user_id, project_id) + if FLAGS.checksum_base_images: + f = open(target, 'r') + checksum = utils.hash_file(f) + f.close() + + checksum_fname = '%s.sha1' % target + fd = os.open(checksum_filename, os.O_WRONLY, mode=0444) + os.write(fd, checksum) + os.close(fd) + @staticmethod def _create_local(target, local_size, unit='G', fs_format=None): """Create a blank image of specified size""" @@ -888,7 +914,7 @@ class LibvirtConnection(driver.ComputeDriver): libvirt_utils.create_image('raw', target, '%dM' % swap_mb) libvirt_utils.mkfs('swap', target) - def _create_image(self, context, inst, libvirt_xml, suffix='', + def _create_image(self, context, instance, libvirt_xml, suffix='', disk_images=None, network_info=None, block_device_info=None): if not suffix: @@ -897,13 +923,13 @@ class LibvirtConnection(driver.ComputeDriver): # syntactic nicety def basepath(fname='', suffix=suffix): return os.path.join(FLAGS.instances_path, - inst['name'], + instance['name'], fname + suffix) # ensure directories exist and are writable libvirt_utils.ensure_tree(basepath(suffix='')) - LOG.info(_('instance %s: Creating image'), inst['name']) + LOG.info(_('Creating image'), instance=instance) libvirt_utils.write_to_file(basepath('libvirt.xml'), libvirt_xml) if FLAGS.libvirt_type == 'lxc': @@ -914,9 +940,9 @@ class LibvirtConnection(driver.ComputeDriver): libvirt_utils.write_to_file(basepath('console.log', ''), '', 007) if not disk_images: - disk_images = {'image_id': inst['image_ref'], - 'kernel_id': inst['kernel_id'], - 'ramdisk_id': inst['ramdisk_id']} + disk_images = {'image_id': instance['image_ref'], + 'kernel_id': instance['kernel_id'], + 'ramdisk_id': instance['ramdisk_id']} if disk_images['kernel_id']: fname = disk_images['kernel_id'] @@ -925,8 +951,8 @@ class LibvirtConnection(driver.ComputeDriver): target=basepath('kernel'), fname=fname, image_id=disk_images['kernel_id'], - user_id=inst['user_id'], - project_id=inst['project_id']) + user_id=instance['user_id'], + project_id=instance['project_id']) if disk_images['ramdisk_id']: fname = disk_images['ramdisk_id'] self._cache_image(fn=libvirt_utils.fetch_image, @@ -934,19 +960,19 @@ class LibvirtConnection(driver.ComputeDriver): target=basepath('ramdisk'), fname=fname, image_id=disk_images['ramdisk_id'], - user_id=inst['user_id'], - project_id=inst['project_id']) + user_id=instance['user_id'], + project_id=instance['project_id']) root_fname = hashlib.sha1(str(disk_images['image_id'])).hexdigest() - size = inst['root_gb'] * 1024 * 1024 * 1024 + size = instance['root_gb'] * 1024 * 1024 * 1024 - inst_type_id = inst['instance_type_id'] + inst_type_id = instance['instance_type_id'] inst_type = instance_types.get_instance_type(inst_type_id) if size == 0 or suffix == '.rescue': size = None root_fname += "_sm" else: - root_fname += "_%d" % inst['root_gb'] + root_fname += "_%d" % instance['root_gb'] if not self._volume_in_mapping(self.default_root_device, block_device_info): @@ -956,31 +982,31 @@ class LibvirtConnection(driver.ComputeDriver): fname=root_fname, cow=FLAGS.use_cow_images, image_id=disk_images['image_id'], - user_id=inst['user_id'], - project_id=inst['project_id'], + user_id=instance['user_id'], + project_id=instance['project_id'], size=size) - ephemeral_gb = inst['ephemeral_gb'] + ephemeral_gb = instance['ephemeral_gb'] if ephemeral_gb and not self._volume_in_mapping( self.default_ephemeral_device, block_device_info): fn = functools.partial(self._create_ephemeral, fs_label='ephemeral0', - os_type=inst.os_type) + os_type=instance.os_type) self._cache_image(fn=fn, target=basepath('disk.local'), fname="ephemeral_%s_%s_%s" % - ("0", ephemeral_gb, inst.os_type), + ("0", ephemeral_gb, instance.os_type), cow=FLAGS.use_cow_images, ephemeral_size=ephemeral_gb) for eph in driver.block_device_info_get_ephemerals(block_device_info): fn = functools.partial(self._create_ephemeral, fs_label='ephemeral%d' % eph['num'], - os_type=inst.os_type) + os_type=instance.os_type) self._cache_image(fn=fn, target=basepath(_get_eph_disk(eph)), fname="ephemeral_%s_%s_%s" % - (eph['num'], eph['size'], inst.os_type), + (eph['num'], eph['size'], instance.os_type), cow=FLAGS.use_cow_images, ephemeral_size=eph['size']) @@ -1005,11 +1031,11 @@ class LibvirtConnection(driver.ComputeDriver): # partitioned disk image where the target partition is the first # partition target_partition = None - if not inst['kernel_id']: + if not instance['kernel_id']: target_partition = "1" - config_drive_id = inst.get('config_drive_id') - config_drive = inst.get('config_drive') + config_drive_id = instance.get('config_drive_id') + config_drive = instance.get('config_drive') if any((FLAGS.libvirt_type == 'lxc', config_drive, config_drive_id)): target_partition = None @@ -1020,14 +1046,14 @@ class LibvirtConnection(driver.ComputeDriver): target=basepath('disk.config'), fname=fname, image_id=config_drive_id, - user_id=inst['user_id'], - project_id=inst['project_id'],) + user_id=instance['user_id'], + project_id=instance['project_id'],) elif config_drive: self._create_local(basepath('disk.config'), 64, unit='M', fs_format='msdos') # 64MB - if inst['key_data']: - key = str(inst['key_data']) + if instance['key_data']: + key = str(instance['key_data']) else: key = None net = None @@ -1069,22 +1095,21 @@ class LibvirtConnection(driver.ComputeDriver): searchList=[{'interfaces': nets, 'use_ipv6': FLAGS.use_ipv6}])) - metadata = inst.get('metadata') + metadata = instance.get('metadata') if any((key, net, metadata)): - inst_name = inst['name'] + instance_name = instance['name'] if config_drive: # Should be True or None by now. injection_path = basepath('disk.config') img_id = 'config-drive' else: injection_path = basepath('disk') - img_id = inst.image_ref + img_id = instance.image_ref for injection in ('metadata', 'key', 'net'): if locals()[injection]: - LOG.info(_('instance %(inst_name)s: injecting ' - '%(injection)s into image %(img_id)s' - % locals())) + LOG.info(_('Injecting %(injection)s into image %(img_id)s' + % locals()), instance=instance) try: disk.inject_data(injection_path, key, net, metadata, partition=target_partition, @@ -1092,8 +1117,9 @@ class LibvirtConnection(driver.ComputeDriver): except Exception as e: # This could be a windows image, or a vmdk format disk - LOG.warn(_('instance %(inst_name)s: ignoring error injecting' - ' data into image %(img_id)s (%(e)s)') % locals()) + LOG.warn(_('Ignoring error injecting data into image ' + '%(img_id)s (%(e)s)') % locals(), + instance=instance) if FLAGS.libvirt_type == 'lxc': self.container = disk.setup_container(basepath('disk'), @@ -1103,9 +1129,10 @@ class LibvirtConnection(driver.ComputeDriver): if FLAGS.libvirt_type == 'uml': libvirt_utils.chown(basepath('disk'), 'root') - files_to_inject = inst.get('injected_files') + files_to_inject = instance.get('injected_files') if files_to_inject: - self._inject_files(inst, files_to_inject) + self._inject_files(instance, files_to_inject, + partition=target_partition) @staticmethod def _volume_in_mapping(mount_device, block_device_info): @@ -1996,6 +2023,10 @@ class LibvirtConnection(driver.ComputeDriver): """Sets the specified host's ability to accept new instances.""" pass + def manage_image_cache(self, context): + """Manage the local cache of images.""" + self.image_cache_manager.verify_base_images(context) + class HostState(object): """Manages information about the compute node through libvirt""" diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index 7045b4abc..0bfd89de1 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -33,6 +33,8 @@ from nova.virt import netutils LOG = logging.getLogger("nova.virt.libvirt.firewall") FLAGS = flags.FLAGS +# The default Firewall driver must be listed at position 0 +drivers = ['nova.virt.libvirt.firewall.IptablesFirewallDriver', ] try: import libvirt diff --git a/nova/virt/libvirt/imagecache.py b/nova/virt/libvirt/imagecache.py new file mode 100644 index 000000000..04b35f1ef --- /dev/null +++ b/nova/virt/libvirt/imagecache.py @@ -0,0 +1,364 @@ +#!/usr/bin/python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Michael Still and Canonical Inc +# 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. + +"""Image cache manager. + +The cache manager implements the specification at +http://wiki.openstack.org/nova-image-cache-management. + +""" + +import datetime +import hashlib +import os +import sys +import time + +from nova import compute +from nova import context as db_context +from nova import db +from nova import flags +from nova import image +from nova import log as logging +from nova.openstack.common import cfg +from nova import utils +from nova.virt.libvirt import utils as virtutils + + +LOG = logging.getLogger('nova.compute.imagecache') + +imagecache_opts = [ + cfg.BoolOpt('remove_unused_base_images', + default=False, + help='Should unused base images be removed?'), + cfg.IntOpt('remove_unused_minimum_age_seconds', + default=3600, + help='Unused base images younger than this will not be ' + 'removed'), + ] + +flags.DECLARE('instances_path', 'nova.compute.manager') +FLAGS = flags.FLAGS +FLAGS.add_options(imagecache_opts) + + +def read_stored_checksum(base_file): + """Read the checksum which was created at image fetch time. + + Returns the checksum (as hex) or None. + """ + checksum_file = '%s.sha1' % base_file + if not os.path.exists(checksum_file): + return None + + f = open(checksum_file, 'r') + stored_checksum = f.read().rstrip() + f.close() + return stored_checksum + + +class ImageCacheManager(object): + def __init__(self): + self.unexplained_images = [] + + def _list_base_images(self, base_dir): + """Return a list of the images present in _base. + + Note that this does not return a value. It instead populates a class + variable with a list of images that we need to try and explain. + """ + # Determine what images we have on disk. There will be other files in + # this directory (for example kernels) so we only grab the ones which + # are the right length to be disk images. + self.unexplained_images = [] + digest_size = hashlib.sha1().digestsize * 2 + for ent in os.listdir(base_dir): + if len(ent) == digest_size or len(ent) == digest_size + 3: + entpath = os.path.join(base_dir, ent) + if os.path.isfile(entpath): + self.unexplained_images.append(entpath) + + def _list_running_instances(self, context): + """List running instances (on all compute nodes).""" + self.used_images = {} + self.image_popularity = {} + + instances = db.instance_get_all(context) + for instance in instances: + image_ref_str = str(instance['image_ref']) + local, remote, insts = self.used_images.get(image_ref_str, + (0, 0, [])) + if instance['host'] == FLAGS.host: + local += 1 + else: + remote += 1 + insts.append(instance['name']) + self.used_images[image_ref_str] = (local, remote, insts) + + self.image_popularity.setdefault(image_ref_str, 0) + self.image_popularity[image_ref_str] += 1 + + def _list_backing_images(self): + """List the backing images currently in use.""" + inuse_images = [] + for ent in os.listdir(FLAGS.instances_path): + if ent.startswith('instance-'): + disk_path = os.path.join(FLAGS.instances_path, ent, 'disk') + if os.path.exists(disk_path): + backing_file = virtutils.get_disk_backing_file(disk_path) + LOG.debug(_('Instance %(instance)s is backed by ' + '%(backing)s'), + {'instance': ent, + 'backing': backing_file}) + + backing_path = os.path.join(FLAGS.instances_path, + '_base', backing_file) + if not backing_path in inuse_images: + inuse_images.append(backing_path) + + if backing_path in self.unexplained_images: + LOG.warning(_('Instance %(instance)s is using a ' + 'backing file %(backing)s which does ' + 'not appear in the image service'), + {'instance': ent, + 'backing': backing_file}) + self.unexplained_images.remove(backing_path) + + return inuse_images + + def _find_base_file(self, base_dir, fingerprint): + """Find the base file matching this fingerprint. + + Yields the name of the base file, and a boolean which is True if + the image is "small". Note that is is possible for more than one + yield to result from this check. + + If no base file is found, then nothing is yielded. + """ + base_file = os.path.join(base_dir, fingerprint) + if os.path.exists(base_file): + yield base_file, False + + base_file = os.path.join(base_dir, fingerprint + '_sm') + if os.path.exists(base_file): + yield base_file, True + + def _verify_checksum(self, img, base_file): + """Compare the checksum stored on disk with the current file. + + Note that if the checksum fails to verify this is logged, but no actual + action occurs. This is something sysadmins should monitor for and + handle manually when it occurs. + """ + f = open(base_file, 'r') + current_checksum = utils.hash_file(f) + f.close() + + stored_checksum = read_stored_checksum(base_file) + + if stored_checksum: + if current_checksum != stored_checksum: + LOG.error(_('%(container_format)s-%(id)s ' + '(%(base_file)s): ' + 'image verification failed'), + {'container_format': img['container_format'], + 'id': img['id'], + 'base_file': base_file}) + return False + + else: + return True + + else: + LOG.debug(_('%(container_format)s-%(id)s (%(base_file)s): ' + 'image verification skipped, no hash stored'), + {'container_format': img['container_format'], + 'id': img['id'], + 'base_file': base_file}) + return None + + def _remove_base_file(self, base_file): + """Remove a single base file if it is old enough. + + Returns nothing. + """ + mtime = os.path.getmtime(base_file) + age = time.time() - mtime + + if age < FLAGS.remove_unused_minimum_age_seconds: + LOG.info(_('Base file too young to remove: %s'), + base_file) + else: + LOG.info(_('Removing base file: %s'), base_file) + try: + os.remove(base_file) + signature = base_file + '.sha1' + if os.path.exists(signature): + os.remove(signature) + except OSError, e: + LOG.error(_('Failed to remove %(base_file)s, ' + 'error was %(error)s'), + {'base_file': base_file, + 'error': e}) + + def _handle_base_image(self, img, base_file, image_small): + """Handle the checks for a single base image.""" + + # TODO(mikal): Write a unit test for this method + image_bad = False + image_in_use = False + + if base_file: + LOG.debug(_('%(container_format)s-%(id)s (%(base_file)s): ' + 'checking'), + {'container_format': img['container_format'], + 'id': img['id'], + 'base_file': base_file}) + + if base_file in self.unexplained_images: + self.unexplained_images.remove(base_file) + self._verify_checksum(img, base_file) + + else: + LOG.debug(_('%(container_format)s-%(id)s (%(base_file)s): ' + 'base file absent'), + {'container_format': img['container_format'], + 'id': img['id'], + 'base_file': base_file}) + base_file = None + + instances = [] + if str(img['id']) in self.used_images: + local, remote, instances = self.used_images[str(img['id'])] + if local > 0: + LOG.debug(_('%(container_format)s-%(id)s (%(base_file)s): ' + 'in use: on this node %(local)d local, ' + '%(remote)d on other nodes'), + {'container_format': img['container_format'], + 'id': img['id'], + 'base_file': base_file, + 'local': local, + 'remote': remote}) + + image_in_use = True + self.active_base_files.append(base_file) + + if not base_file: + LOG.warning(_('%(container_format)s-%(id)s ' + '(%(base_file)s): warning -- an absent ' + 'base file is in use! instances: ' + '%(instance_list)s'), + {'container_format': + img['container_format'], + 'id': img['id'], + 'base_file': base_file, + 'instance_list': ' '.join(instances)}) + + else: + LOG.debug(_('%(container_format)s-%(id)s (%(base_file)s): ' + 'in: on other nodes (%(remote)d on other ' + 'nodes)'), + {'container_format': img['container_format'], + 'id': img['id'], + 'base_file': base_file, + 'remote': remote}) + if image_bad: + self.corrupt_base_files.append(base_file) + + if base_file: + if not image_in_use: + LOG.debug(_('%(container_format)s-%(id)s (%(base_file)s): ' + 'image is not in use'), + {'container_format': img['container_format'], + 'id': img['id'], + 'base_file': base_file}) + self.removable_base_files.append(base_file) + + else: + LOG.debug(_('%(container_format)s-%(id)s (%(base_file)s): ' + 'image is in use'), + {'container_format': img['container_format'], + 'id': img['id'], + 'base_file': base_file}) + + def verify_base_images(self, context): + """Verify that base images are in a reasonable state.""" + # TODO(mikal): Write a unit test for this method + + base_dir = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base_dir): + LOG.debug(_('Skipping verification, no base directory at %s'), + base_dir) + return + + LOG.debug(_('Verify base images')) + self._list_base_images(base_dir) + self._list_running_instances(context) + + # Determine what images are in glance + image_service = image.get_default_image_service() + + self.active_base_files = [] + self.corrupt_base_files = [] + self.removable_base_files = [] + + # GlanceImageService.detail uses _fetch_images which handles pagination + # for us + for img in image_service.detail(context): + if img['container_format'] != 'ami': + continue + + fingerprint = hashlib.sha1(str(img['id'])).hexdigest() + for base_file, image_small in self._find_base_file(base_dir, + fingerprint): + self._handle_base_image(img, base_file, image_small) + + # Elements remaining in unexplained_images are not currently in + # glance. That doesn't mean that they're really not in use though + # (consider images which have been removed from glance but are still + # used by instances). So, we check the backing file for any running + # instances as well. + if self.unexplained_images: + inuse_backing_images = self._list_backing_images() + if inuse_backing_images: + for backing_path in inuse_backing_images: + self.active_base_files.append(backing_path) + + # Anything left is an unknown base image + for img in self.unexplained_images: + LOG.warning(_('Unknown base file: %s'), img) + self.removable_base_files.append(img) + + # Dump these lists + if self.active_base_files: + LOG.info(_('Active base files: %s'), + ' '.join(self.active_base_files)) + if self.corrupt_base_files: + LOG.info(_('Corrupt base files: %s'), + ' '.join(self.corrupt_base_files)) + + if self.removable_base_files: + LOG.info(_('Removable base files: %s'), + ' '.join(self.removable_base_files)) + + if FLAGS.remove_unused_base_images: + for base_file in self.removable_base_files: + self._remove_base_file(base_file) + + # That's it + LOG.debug(_('Verification complete')) diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py index 65254936a..f96128124 100644 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -23,9 +23,9 @@ import os import random import shutil -from nova.common import cfg from nova import exception from nova import flags +from nova.openstack.common import cfg from nova import utils from nova.virt.disk import api as disk from nova.virt import images @@ -44,6 +44,16 @@ def execute(*args, **kwargs): return utils.execute(*args, **kwargs) +def get_iscsi_initiator(): + """Get iscsi initiator name for this machine""" + # NOTE(vish) openiscsi stores initiator name in a file that + # needs root permission to read. + contents = utils.read_file_as_root('/etc/iscsi/initiatorname.iscsi') + for l in contents.split('\n'): + if l.startswith('InitiatorName='): + return l[l.index('=') + 1:].strip() + + def create_image(disk_format, path, size): """Create a disk image diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 503c33d0f..344dae573 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -19,11 +19,11 @@ """VIF drivers for libvirt.""" -from nova.common import cfg from nova import exception from nova import flags from nova import log as logging from nova.network import linux_net +from nova.openstack.common import cfg from nova import utils from nova.virt import netutils from nova.virt.vif import VIFDriver @@ -82,13 +82,14 @@ class LibvirtBridgeDriver(VIFDriver): if (not network.get('multi_host') and mapping.get('should_create_bridge')): if mapping.get('should_create_vlan'): + iface = FLAGS.vlan_interface or network['bridge_interface'] LOG.debug(_('Ensuring vlan %(vlan)s and bridge %(bridge)s'), {'vlan': network['vlan'], 'bridge': network['bridge']}) linux_net.LinuxBridgeInterfaceDriver.ensure_vlan_bridge( network['vlan'], network['bridge'], - network['bridge_interface']) + iface) else: LOG.debug(_("Ensuring bridge %s"), network['bridge']) linux_net.LinuxBridgeInterfaceDriver.ensure_bridge( diff --git a/nova/virt/vmwareapi/vim.py b/nova/virt/vmwareapi/vim.py index 0ce1be645..648231087 100644 --- a/nova/virt/vmwareapi/vim.py +++ b/nova/virt/vmwareapi/vim.py @@ -26,8 +26,8 @@ try: except ImportError: suds = None -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg from nova.virt.vmwareapi import error_util RESP_NOT_XML_ERROR = 'Response is "text/html", not "text/xml"' diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 172936def..af867dc6a 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -26,11 +26,11 @@ import urllib import urllib2 import uuid -from nova.common import cfg from nova.compute import power_state from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.virt.vmwareapi import vim_util from nova.virt.vmwareapi import vm_util diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index d71b922c9..909969fac 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -36,12 +36,12 @@ import time from eventlet import event -from nova.common import cfg from nova import context from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.virt import driver from nova.virt.vmwareapi import error_util @@ -178,6 +178,15 @@ class VMWareESXConnection(driver.ComputeDriver): """Return link to instance's ajax console.""" return self._vmops.get_ajax_console(instance) + def get_volume_connector(self, _instance): + """Return volume connector information""" + # TODO(vish): When volume attaching is supported, return the + # proper initiator iqn. + return { + 'ip': FLAGS.vmwareapi_host_ip, + 'initiator': None + } + def attach_volume(self, connection_info, instance_name, mountpoint): """Attach volume storage to VM instance.""" pass diff --git a/nova/virt/xenapi/firewall.py b/nova/virt/xenapi/firewall.py index 823115220..dc75aa46a 100644 --- a/nova/virt/xenapi/firewall.py +++ b/nova/virt/xenapi/firewall.py @@ -31,6 +31,10 @@ from nova.virt.firewall import IptablesFirewallDriver LOG = logging.getLogger("nova.virt.xenapi.firewall") FLAGS = flags.FLAGS +# The default Firewall driver must be listed at position 0 +drivers = ['nova.virt.firewall.IptablesFirewallDriver', + 'nova.virt.xenapi.firewall.Dom0IptablesFirewallDriver', ] + class Dom0IptablesFirewallDriver(IptablesFirewallDriver): """ Dom0IptablesFirewallDriver class diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py index c1b752d5a..f5b086aaa 100644 --- a/nova/virt/xenapi/vif.py +++ b/nova/virt/xenapi/vif.py @@ -19,9 +19,9 @@ """VIF drivers for XenAPI.""" -from nova.common import cfg from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova.virt.vif import VIFDriver from nova.virt.xenapi.network_utils import NetworkHelper from nova.virt.xenapi.vm_utils import VMHelper @@ -77,7 +77,7 @@ class XenAPIBridgeDriver(XenVIFDriver): vlan_num = network['vlan'] bridge = network['bridge'] - bridge_interface = network['bridge_interface'] + bridge_interface = FLAGS.vlan_interface or network['bridge_interface'] # Check whether bridge already exists # Retrieve network whose name_label is "bridge" network_ref = NetworkHelper.find_network_with_name_label( diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9ddf8b22a..638042e35 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -33,11 +33,11 @@ import uuid from decimal import Decimal, InvalidOperation from xml.dom import minidom -from nova.common import cfg from nova import exception from nova import flags from nova.image import glance from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.compute import instance_types from nova.compute import power_state @@ -513,7 +513,7 @@ class VMHelper(HelperBase): # NOTE(jk0): We use a FAT32 filesystem for the Windows swap # partition because that is what parted supports. is_windows = instance.os_type == "windows" - fs_type = "fat32" if is_windows else "linux-swap" + fs_type = "vfat" if is_windows else "linux-swap" cls._generate_disk(session, instance, vm_ref, userdevice, 'swap', swap_mb, fs_type) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c7f713ea2..c2960b86a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -31,7 +31,6 @@ import uuid from eventlet import greenthread -from nova.common import cfg from nova.compute import api as compute from nova.compute import power_state from nova import context as nova_context @@ -39,11 +38,14 @@ from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.virt import driver -from nova.virt.xenapi import volume_utils +from nova.virt.xenapi import firewall from nova.virt.xenapi import network_utils from nova.virt.xenapi import vm_utils +from nova.virt.xenapi import volume_utils + VolumeHelper = volume_utils.VolumeHelper NetworkHelper = network_utils.NetworkHelper @@ -104,6 +106,8 @@ class VMOps(object): self._session = session self.poll_rescue_last_ran = None VMHelper.XenAPI = self.XenAPI + if FLAGS.firewall_driver not in firewall.drivers: + FLAGS['firewall_driver'].SetDefault(firewall.drivers[0]) fw_class = utils.import_class(FLAGS.firewall_driver) self.firewall_driver = fw_class(xenapi_session=self._session) vif_impl = utils.import_class(FLAGS.xenapi_vif_driver) @@ -418,10 +422,14 @@ class VMOps(object): # Attach any other disks for vdi in vdis[1:]: - if generate_swap and vdi['vdi_type'] == 'swap': - continue vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi['vdi_uuid']) + + if generate_swap and vdi['vdi_type'] == 'swap': + # We won't be using it, so don't let it leak + VMHelper.destroy_vdi(self._session, vdi_ref) + continue + VolumeHelper.create_vbd(session=self._session, vm_ref=vm_ref, vdi_ref=vdi_ref, userdevice=userdevice, bootable=False) @@ -642,7 +650,7 @@ class VMOps(object): #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added - logging.debug(_("Starting snapshot for VM %s"), instance) + logging.debug(_("Starting snapshot for VM %s"), instance['uuid']) vm_ref = VMHelper.lookup(self._session, instance.name) label = "%s-snapshot" % instance.name diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 6fda205d0..e034cb3ca 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -70,13 +70,13 @@ from eventlet import queue from eventlet import tpool from eventlet import timeout -from nova.common import cfg from nova import context from nova import db from nova import exception from nova import utils from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova.virt import driver from nova.virt.xenapi import vm_utils from nova.virt.xenapi.vmops import VMOps @@ -179,6 +179,7 @@ class XenAPIConnection(driver.ComputeDriver): self._host_state = None self._product_version = self._session.get_product_version() self._vmops = VMOps(self._session, self._product_version) + self._initiator = None @property def host_state(self): @@ -327,13 +328,9 @@ class XenAPIConnection(driver.ComputeDriver): for iusage in self._vmops.get_all_bw_usage(start_time, stop_time).\ values(): for macaddr, usage in iusage.iteritems(): - vi = db.virtual_interface_get_by_address( - context.get_admin_context(), - macaddr) - if vi: - bwusage.append(dict(virtual_interface=vi, - bw_in=usage['bw_in'], - bw_out=usage['bw_out'])) + bwusage.append(dict(mac_address=macaddr, + bw_in=usage['bw_in'], + bw_out=usage['bw_out'])) return bwusage def get_console_output(self, instance): @@ -348,6 +345,20 @@ class XenAPIConnection(driver.ComputeDriver): """Return link to instance's ajax console""" return self._vmops.get_vnc_console(instance) + def get_volume_connector(self, _instance): + """Return volume connector information""" + if not self._initiator: + stats = self.get_host_stats(update=True) + try: + self._initiator = stats['host_other-config']['iscsi_iqn'] + except (TypeError, KeyError): + LOG.warn(_('Could not determine iscsi initiator name')) + self._initiator = None + return { + 'ip': self.get_host_ip_addr(), + 'initiator': self._initiator + } + @staticmethod def get_host_ip_addr(): xs_url = urlparse.urlparse(FLAGS.xenapi_connection_url) diff --git a/nova/vnc/__init__.py b/nova/vnc/__init__.py index 0b2581203..ba7268e97 100644 --- a/nova/vnc/__init__.py +++ b/nova/vnc/__init__.py @@ -18,8 +18,8 @@ """Module for VNC Proxying.""" -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg vnc_opts = [ diff --git a/nova/vnc/xvp_proxy.py b/nova/vnc/xvp_proxy.py index bf9ee5975..dfaf9a47a 100644 --- a/nova/vnc/xvp_proxy.py +++ b/nova/vnc/xvp_proxy.py @@ -26,10 +26,10 @@ import eventlet.green import eventlet.greenio import eventlet.wsgi -from nova.common import cfg from nova import context from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import rpc from nova import version from nova import wsgi diff --git a/nova/volume/api.py b/nova/volume/api.py index cc9cac019..d5042833e 100644 --- a/nova/volume/api.py +++ b/nova/volume/api.py @@ -244,22 +244,22 @@ class API(base.Base): "args": {"volume_id": volume['id']}}) @wrap_check_policy - def initialize_connection(self, context, volume, address): + def initialize_connection(self, context, volume, connector): host = volume['host'] queue = self.db.queue_get_for(context, FLAGS.volume_topic, host) return rpc.call(context, queue, {"method": "initialize_connection", "args": {"volume_id": volume['id'], - "address": address}}) + "connector": connector}}) @wrap_check_policy - def terminate_connection(self, context, volume, address): + def terminate_connection(self, context, volume, connector): host = volume['host'] queue = self.db.queue_get_for(context, FLAGS.volume_topic, host) return rpc.call(context, queue, {"method": "terminate_connection", "args": {"volume_id": volume['id'], - "address": address}}) + "connector": connector}}) def _create_snapshot(self, context, volume, name, description, force=False): diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 0505b89b1..f00eb0800 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -24,10 +24,10 @@ import os import time from xml.etree import ElementTree -from nova.common import cfg from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.volume import iscsi from nova.volume import volume_types @@ -215,12 +215,12 @@ class VolumeDriver(object): """Make sure volume is exported.""" raise NotImplementedError() - def initialize_connection(self, volume, address): - """Allow connection to ip and return connection info.""" + def initialize_connection(self, volume, connector): + """Allow connection to connector and return connection info.""" raise NotImplementedError() - def terminate_connection(self, volume, address): - """Disallow connection from ip""" + def terminate_connection(self, volume, connector): + """Disallow connection from connector""" raise NotImplementedError() def get_volume_stats(self, refresh=False): @@ -409,7 +409,7 @@ class ISCSIDriver(VolumeDriver): '-v', property_value) return self._run_iscsiadm(iscsi_properties, iscsi_command) - def initialize_connection(self, volume, address): + def initialize_connection(self, volume, connector): """Initializes the connection and returns connection info. The iscsi driver returns a driver_volume_type of 'iscsi'. @@ -433,7 +433,7 @@ class ISCSIDriver(VolumeDriver): 'data': iscsi_properties } - def terminate_connection(self, volume, address): + def terminate_connection(self, volume, connector): pass def check_for_export(self, context, volume_id): @@ -461,13 +461,13 @@ class FakeISCSIDriver(ISCSIDriver): """No setup necessary in fake mode.""" pass - def initialize_connection(self, volume, address): + def initialize_connection(self, volume, connector): return { 'driver_volume_type': 'iscsi', 'data': {} } - def terminate_connection(self, volume, address): + def terminate_connection(self, volume, connector): pass @staticmethod @@ -532,7 +532,7 @@ class RBDDriver(VolumeDriver): """Removes an export for a logical volume""" pass - def initialize_connection(self, volume, address): + def initialize_connection(self, volume, connector): return { 'driver_volume_type': 'rbd', 'data': { @@ -540,7 +540,7 @@ class RBDDriver(VolumeDriver): } } - def terminate_connection(self, volume, address): + def terminate_connection(self, volume, connector): pass @@ -601,7 +601,7 @@ class SheepdogDriver(VolumeDriver): """Removes an export for a logical volume""" pass - def initialize_connection(self, volume, address): + def initialize_connection(self, volume, connector): return { 'driver_volume_type': 'sheepdog', 'data': { @@ -609,7 +609,7 @@ class SheepdogDriver(VolumeDriver): } } - def terminate_connection(self, volume, address): + def terminate_connection(self, volume, connector): pass @@ -638,10 +638,10 @@ class LoggingVolumeDriver(VolumeDriver): def remove_export(self, context, volume): self.log_action('remove_export', volume) - def initialize_connection(self, volume, address): + def initialize_connection(self, volume, connector): self.log_action('initialize_connection', volume) - def terminate_connection(self, volume, address): + def terminate_connection(self, volume, connector): self.log_action('terminate_connection', volume) def check_for_export(self, context, volume_id): diff --git a/nova/volume/iscsi.py b/nova/volume/iscsi.py index 66c293f15..f0934eb96 100644 --- a/nova/volume/iscsi.py +++ b/nova/volume/iscsi.py @@ -20,8 +20,8 @@ Helper code for the iSCSI volume driver. """ -from nova.common import cfg from nova import flags +from nova.openstack.common import cfg from nova import utils diff --git a/nova/volume/manager.py b/nova/volume/manager.py index f22fcc2ca..15f26f79e 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -38,12 +38,12 @@ intact. """ -from nova.common import cfg from nova import context from nova import exception from nova import flags from nova import log as logging from nova import manager +from nova.openstack.common import cfg from nova import rpc from nova import utils from nova.volume import volume_types @@ -254,31 +254,51 @@ class VolumeManager(manager.SchedulerDependentManager): # TODO(vish): refactor this into a more general "unreserve" self.db.volume_detached(context, volume_id) - def initialize_connection(self, context, volume_id, address): - """Initialize volume to be connected from address. + def initialize_connection(self, context, volume_id, connector): + """Prepare volume for connection from host represented by connector. This method calls the driver initialize_connection and returns - it to the caller. The driver is responsible for doing any - necessary security setup and returning a connection_info dictionary - in the following format: - {'driver_volume_type': driver_volume_type - 'data': data} + it to the caller. The connector parameter is a dictionary with + information about the host that will connect to the volume in the + following format: + { + 'ip': ip, + 'initiator': initiator, + } + + ip: the ip address of the connecting machine + + initiator: the iscsi initiator name of the connecting machine. + This can be None if the connecting machine does not support iscsi + connections. + + driver is responsible for doing any necessary security setup and + returning a connection_info dictionary in the following format: + { + 'driver_volume_type': driver_volume_type, + 'data': data, + } driver_volume_type: a string to identify the type of volume. This can be used by the calling code to determine the strategy for connecting to the volume. This could be 'iscsi', 'rbd', 'sheepdog', etc. + data: this is the data that the calling code will use to connect to the volume. Keep in mind that this will be serialized to json in various places, so it should not contain any non-json data types. """ volume_ref = self.db.volume_get(context, volume_id) - return self.driver.initialize_connection(volume_ref, address) + return self.driver.initialize_connection(volume_ref, connector) + + def terminate_connection(self, context, volume_id, connector): + """Cleanup connection from host represented by connector. - def terminate_connection(self, context, volume_id, address): + The format of connector is the same as for initialize_connection. + """ volume_ref = self.db.volume_get(context, volume_id) - self.driver.terminate_connection(volume_ref, address) + self.driver.terminate_connection(volume_ref, connector) def check_for_export(self, context, instance_id): """Make sure whether volume is exported.""" diff --git a/nova/volume/san.py b/nova/volume/san.py index 25309768e..bc06d7e03 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -32,11 +32,10 @@ import string import uuid from xml.etree import ElementTree -from nova.common import cfg - from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import utils from nova.utils import ssh_execute from nova.volume.driver import ISCSIDriver diff --git a/nova/volume/xensm.py b/nova/volume/xensm.py index bf219f273..a706b10be 100644 --- a/nova/volume/xensm.py +++ b/nova/volume/xensm.py @@ -203,7 +203,7 @@ class XenSMDriver(VolumeDriver): """Safely, synchronously recreates an export for a logical volume.""" pass - def initialize_connection(self, volume, address): + def initialize_connection(self, volume, connector): try: xensm_properties = dict(self.db.sm_volume_get(self.ctxt, volume['id'])) @@ -236,5 +236,5 @@ class XenSMDriver(VolumeDriver): 'data': xensm_properties } - def terminate_connection(self, volume, address): + def terminate_connection(self, volume, connector): pass diff --git a/nova/vsa/api.py b/nova/vsa/api.py index 9c2bc0ce2..08b9deb13 100644 --- a/nova/vsa/api.py +++ b/nova/vsa/api.py @@ -25,11 +25,11 @@ For assistance and guidelines pls contact import sys -from nova.common import cfg from nova import compute from nova import exception from nova import flags from nova import log as logging +from nova.openstack.common import cfg from nova import rpc from nova import volume from nova.compute import instance_types diff --git a/nova/vsa/manager.py b/nova/vsa/manager.py index 42b5b1032..4dab8e31f 100644 --- a/nova/vsa/manager.py +++ b/nova/vsa/manager.py @@ -22,12 +22,12 @@ Handles all processes relating to Virtual Storage Arrays (VSA). """ -from nova.common import cfg from nova import compute from nova import exception from nova import flags from nova import log as logging from nova import manager +from nova.openstack.common import cfg from nova import volume from nova import vsa from nova import utils diff --git a/po/pt_BR.po b/po/pt_BR.po index 24fd24d55..ff44b8092 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-02-21 10:03-0500\n" -"PO-Revision-Date: 2012-01-18 19:48+0000\n" -"Last-Translator: Rafael Neri <Unknown>\n" +"PO-Revision-Date: 2012-02-01 12:10+0000\n" +"Last-Translator: Tiago Hillebrandt <tiagoscd@yahoo.com.br>\n" "Language-Team: Brazilian Portuguese <pt_BR@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-01-19 05:06+0000\n" -"X-Generator: Launchpad (build 14692)\n" +"X-Launchpad-Export-Date: 2012-02-02 06:17+0000\n" +"X-Generator: Launchpad (build 14738)\n" #: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 #: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 @@ -56,22 +56,22 @@ msgstr "Exceção não capturada" msgid "Quota exceeeded for %(pid)s, tried to create %(size)sG volume" msgstr "Cota excedida para %(pid)s, tentando criar volume com %(size)sG" -#: ../nova/volume/api.py:57 +#: ../nova/volume/api.py:87 #, python-format msgid "Volume quota exceeded. You cannot create a volume of size %sG" msgstr "" "Cota excedida para o volume. Você não pode criar um volume com o tamanho %sG" -#: ../nova/volume/api.py:102 ../nova/volume/api.py:172 -#: ../nova/volume/api.py:230 +#: ../nova/volume/api.py:134 ../nova/volume/api.py:210 +#: ../nova/volume/api.py:269 msgid "Volume status must be available" msgstr "O status do volume parece estar disponível" -#: ../nova/volume/api.py:174 +#: ../nova/volume/api.py:212 msgid "Volume is already attached" msgstr "O Volume já está anexado" -#: ../nova/volume/api.py:180 +#: ../nova/volume/api.py:218 msgid "Volume is already detached" msgstr "O Volume já está desanexado" @@ -92,12 +92,12 @@ msgstr "%(param)s propriedade não foi encontrada para %(_image_id)s" msgid "No keypairs defined" msgstr "Os keypairs não foram definidos" -#: ../nova/api/openstack/v2/contrib/admin_actions.py:177 +#: ../nova/api/openstack/compute/contrib/admin_actions.py:185 #, python-format msgid "Compute.api::lock %s" msgstr "Compute.api::lock %s" -#: ../nova/api/openstack/v2/contrib/admin_actions.py:193 +#: ../nova/api/openstack/compute/contrib/admin_actions.py:203 #, python-format msgid "Compute.api::unlock %s" msgstr "Compute.api::unlock %s" @@ -107,27 +107,27 @@ msgstr "Compute.api::unlock %s" msgid "Compute.api::get_lock %s" msgstr "Compute.api::get_lock %s" -#: ../nova/api/openstack/v2/contrib/admin_actions.py:145 +#: ../nova/api/openstack/compute/contrib/admin_actions.py:149 #, python-format msgid "Compute.api::reset_network %s" msgstr "Compute.api::reset_network %s" -#: ../nova/api/openstack/v2/contrib/admin_actions.py:64 +#: ../nova/api/openstack/compute/contrib/admin_actions.py:58 #, python-format msgid "Compute.api::pause %s" msgstr "Compute.api::pause %s" -#: ../nova/api/openstack/v2/contrib/admin_actions.py:81 +#: ../nova/api/openstack/compute/contrib/admin_actions.py:77 #, python-format msgid "Compute.api::unpause %s" msgstr "Compute.api::unpause %s" -#: ../nova/api/openstack/v2/contrib/admin_actions.py:98 +#: ../nova/api/openstack/compute/contrib/admin_actions.py:96 #, python-format msgid "compute.api::suspend %s" msgstr "compute.api::suspend %s" -#: ../nova/api/openstack/v2/contrib/admin_actions.py:115 +#: ../nova/api/openstack/compute/contrib/admin_actions.py:115 #, python-format msgid "compute.api::resume %s" msgstr "compute.api::resume %s" @@ -191,17 +191,17 @@ msgid "Mountpoint %(mountpoint)s detached from instance %(instance_name)s" msgstr "" "Ponto de montagem %(mountpoint)s desanexada da instância %(instance_name)s" -#: ../nova/compute/instance_types.py:115 ../nova/compute/instance_types.py:127 -#: ../nova/compute/instance_types.py:141 ../nova/compute/instance_types.py:152 +#: ../nova/compute/instance_types.py:122 ../nova/compute/instance_types.py:134 +#: ../nova/compute/instance_types.py:148 ../nova/compute/instance_types.py:159 #, python-format msgid "Unknown instance type: %s" msgstr "Tipo de instância desconhecido: %s" -#: ../nova/crypto.py:50 +#: ../nova/crypto.py:51 msgid "Filename of root CA" msgstr "Nome do arquivo da CA raiz" -#: ../nova/crypto.py:53 +#: ../nova/crypto.py:54 msgid "Filename of private key" msgstr "Nome do arquivo da chave privada" @@ -209,25 +209,25 @@ msgstr "Nome do arquivo da chave privada" msgid "Filename of root Certificate Revokation List" msgstr "Nome de arquivo da Lista de Revogação de Certificados" -#: ../nova/crypto.py:57 +#: ../nova/crypto.py:58 msgid "Where we keep our keys" msgstr "Aonde armazenamos nossas chaves" -#: ../nova/crypto.py:59 +#: ../nova/crypto.py:60 msgid "Where we keep our root CA" msgstr "Onde mantemos nosso CA raiz" -#: ../nova/crypto.py:61 +#: ../nova/crypto.py:62 msgid "Should we use a CA for each project?" msgstr "Devemos usar um CA para cada projeto?" -#: ../nova/crypto.py:65 +#: ../nova/crypto.py:66 #, python-format msgid "Subject for certificate for users, %s for project, user, timestamp" msgstr "" "Assunto do certificado para usuários, %s para projeto, usuário, timestamp" -#: ../nova/crypto.py:70 +#: ../nova/crypto.py:71 #, python-format msgid "Subject for certificate for projects, %s for project, timestamp" msgstr "Assunto do certificado para projetos, %s para projeto, timestamp" @@ -237,7 +237,7 @@ msgstr "Assunto do certificado para projetos, %s para projeto, timestamp" msgid "Subject for certificate for vpns, %s for project, timestamp" msgstr "Assunto do certificado para vpns, %s para projeto, timestamp" -#: ../nova/crypto.py:277 +#: ../nova/crypto.py:310 #, python-format msgid "Flags path: %s" msgstr "Localização dos sinalizadores: %s" @@ -247,7 +247,7 @@ msgstr "Localização dos sinalizadores: %s" msgid "Casting to %(topic)s %(host)s for %(method)s" msgstr "Moldagem para %(topic)s %(host)s para %(method)s" -#: ../nova/compute/manager.py:111 +#: ../nova/compute/manager.py:113 #, python-format msgid "check_instance_lock: decorating: |%s|" msgstr "check_instance_lock: decorating: |%s|" @@ -259,36 +259,36 @@ msgid "" msgstr "" "check_instance_lock: argumentos: |%(self)s| |%(context)s| |%(instance_id)s|" -#: ../nova/compute/manager.py:117 +#: ../nova/compute/manager.py:119 #, python-format msgid "check_instance_lock: locked: |%s|" msgstr "check_instance_lock: locked: |%s|" -#: ../nova/compute/manager.py:119 +#: ../nova/compute/manager.py:121 #, python-format msgid "check_instance_lock: admin: |%s|" msgstr "check_instance_lock: admin: |%s|" -#: ../nova/compute/manager.py:124 +#: ../nova/compute/manager.py:126 #, python-format msgid "check_instance_lock: executing: |%s|" msgstr "check_instance_lock: executando: |%s|" -#: ../nova/compute/manager.py:128 +#: ../nova/compute/manager.py:130 #, python-format msgid "check_instance_lock: not executing |%s|" msgstr "check_instance_lock: not executando |%s|" -#: ../nova/compute/manager.py:395 +#: ../nova/compute/manager.py:404 msgid "Instance has already been created" msgstr "A instância já foi criada" -#: ../nova/compute/manager.py:452 +#: ../nova/compute/manager.py:461 #, python-format msgid "instance %s: starting..." msgstr "instância %s: iniciando..." -#: ../nova/virt/xenapi/vmops.py:233 +#: ../nova/virt/xenapi/vmops.py:234 #, python-format msgid "instance %s: Failed to spawn" msgstr "instância %s: falha na geração" @@ -303,12 +303,12 @@ msgstr "Terminando a instância %s" msgid "Deallocating address %s" msgstr "Desalocando o endereço %s" -#: ../nova/compute/manager.py:589 +#: ../nova/compute/manager.py:601 #, python-format msgid "trying to destroy already destroyed instance: %s" msgstr "tentando destruir instância já destruida: %s" -#: ../nova/compute/manager.py:753 +#: ../nova/compute/manager.py:762 #, python-format msgid "Rebooting instance %s" msgstr "Reiniciando a instância %s" @@ -322,7 +322,7 @@ msgstr "" "tentando reiniciar uma instancia com estado diferente de running: " "%(instance_id)s (estado: %(state)s esperado: %(running)s)" -#: ../nova/compute/manager.py:813 +#: ../nova/compute/manager.py:826 #, python-format msgid "instance %s: snapshotting" msgstr "instância %s: fazendo um snapshot" @@ -365,67 +365,67 @@ msgstr "" msgid "instance %(nm)s: injecting file to %(plain_path)s" msgstr "instância %(nm)s: injetando um arquivo para %(plain_path)s" -#: ../nova/compute/manager.py:990 +#: ../nova/compute/manager.py:1005 #, python-format msgid "instance %s: rescuing" msgstr "instância %s: resgatando" -#: ../nova/compute/manager.py:1013 +#: ../nova/compute/manager.py:1029 #, python-format msgid "instance %s: unrescuing" msgstr "instância %s: desfazendo o resgate" -#: ../nova/compute/manager.py:1302 +#: ../nova/compute/manager.py:1324 #, python-format msgid "instance %s: pausing" msgstr "instância %s: pausando" -#: ../nova/compute/manager.py:1320 +#: ../nova/compute/manager.py:1342 #, python-format msgid "instance %s: unpausing" msgstr "instância %s: saindo do pause" -#: ../nova/compute/manager.py:1349 +#: ../nova/compute/manager.py:1371 #, python-format msgid "instance %s: retrieving diagnostics" msgstr "instância %s: recuperando os diagnósticos" -#: ../nova/compute/manager.py:1358 +#: ../nova/compute/manager.py:1380 #, python-format msgid "instance %s: suspending" msgstr "instância %s: suspendendo" -#: ../nova/compute/manager.py:1380 +#: ../nova/compute/manager.py:1402 #, python-format msgid "instance %s: resuming" msgstr "instância %s: resumindo" -#: ../nova/compute/manager.py:1403 +#: ../nova/compute/manager.py:1425 #, python-format msgid "instance %s: locking" msgstr "instância %s: bloqueando" -#: ../nova/compute/manager.py:1412 +#: ../nova/compute/manager.py:1434 #, python-format msgid "instance %s: unlocking" msgstr "instância %s: desbloqueando" -#: ../nova/compute/manager.py:1420 +#: ../nova/compute/manager.py:1442 #, python-format msgid "instance %s: getting locked state" msgstr "instância %s: obtendo estado de bloqueio" -#: ../nova/compute/manager.py:1430 +#: ../nova/compute/manager.py:1452 #, python-format msgid "instance %s: reset network" msgstr "instância %s: reset da rede" -#: ../nova/compute/manager.py:1452 ../nova/api/ec2/cloud.py:823 +#: ../nova/compute/manager.py:1475 ../nova/api/ec2/cloud.py:781 #, python-format msgid "Get console output for instance %s" msgstr "Obter saída do console para instância %s" -#: ../nova/compute/manager.py:1477 +#: ../nova/compute/manager.py:1500 #, python-format msgid "instance %s: getting ajax console" msgstr "instância %s: obtendo console ajax" @@ -457,7 +457,7 @@ msgstr "" "Desconectando volume %(volume_id)s do ponto de montagem %(mp)s na instância " "%(instance_id)s" -#: ../nova/compute/manager.py:1559 +#: ../nova/compute/manager.py:1603 #, python-format msgid "Detaching volume from unknown instance %s" msgstr "Desconectando volume da instância desconhecida %s" @@ -484,55 +484,55 @@ msgstr "Todos os hosts tem muitos gigabytes" msgid "All hosts have too many networks" msgstr "Todos os hosts tem muitas interfaces de rede" -#: ../nova/volume/manager.py:89 +#: ../nova/volume/manager.py:87 #, python-format msgid "Re-exporting %s volumes" msgstr "Re-exportando %s volumes" -#: ../nova/volume/manager.py:94 +#: ../nova/volume/manager.py:92 #, python-format msgid "volume %s: skipping export" msgstr "volume %s: ignorando export" -#: ../nova/volume/manager.py:100 +#: ../nova/volume/manager.py:98 #, python-format msgid "volume %s: creating" msgstr "volume %s: criando" -#: ../nova/volume/manager.py:112 +#: ../nova/volume/manager.py:110 #, python-format msgid "volume %(vol_name)s: creating lv of size %(vol_size)sG" msgstr "volume %(vol_name)s: criando lv com tamanho %(vol_size)sG" -#: ../nova/volume/manager.py:124 +#: ../nova/volume/manager.py:122 #, python-format msgid "volume %s: creating export" msgstr "volume %s: criando o export" -#: ../nova/volume/manager.py:138 +#: ../nova/volume/manager.py:136 #, python-format msgid "volume %s: created successfully" msgstr "volume %s: criado com sucesso" -#: ../nova/volume/manager.py:167 +#: ../nova/volume/manager.py:165 msgid "Volume is still attached" msgstr "O volume continua atachado" -#: ../nova/volume/manager.py:169 +#: ../nova/volume/manager.py:167 msgid "Volume is not local to this node" msgstr "O volume não pertence à este node" -#: ../nova/volume/manager.py:173 +#: ../nova/volume/manager.py:171 #, python-format msgid "volume %s: removing export" msgstr "volume %s: removendo export" -#: ../nova/volume/manager.py:175 +#: ../nova/volume/manager.py:173 #, python-format msgid "volume %s: deleting" msgstr "volume %s: removendo" -#: ../nova/volume/manager.py:190 +#: ../nova/volume/manager.py:188 #, python-format msgid "volume %s: deleted successfully" msgstr "volume %s: remoção realizada com sucesso" @@ -568,8 +568,8 @@ msgid "" "xenapi.fake does not have an implementation for %s or it has been called " "with the wrong number of arguments" msgstr "" -"xenapi.fake não tem implementação para %s ou isto foi chamado com um número " -"de argumentos inválidos" +"xenapi.fake não tem implementação para %s ou foi chamado com um número de " +"argumentos inválido" #: ../nova/tests/test_cloud.py:256 msgid "Can't test instances without a real virtual env." @@ -584,34 +584,34 @@ msgstr "É necessário assistir a instância %s até ela estar rodando..." msgid "Failed to open connection to the hypervisor" msgstr "Falha ao abrir a conexão com o hypervisor" -#: ../nova/network/linux_net.py:937 +#: ../nova/network/linux_net.py:963 #, python-format msgid "Starting VLAN inteface %s" msgstr "Iniciando a VLAN %s" -#: ../nova/network/linux_net.py:969 +#: ../nova/network/linux_net.py:995 #, python-format msgid "Starting Bridge interface for %s" msgstr "Iniciando a Bridge para %s" #. pylint: disable=W0703 -#: ../nova/network/linux_net.py:669 +#: ../nova/network/linux_net.py:698 #, python-format msgid "Hupping dnsmasq threw %s" msgstr "" -#: ../nova/network/linux_net.py:671 +#: ../nova/network/linux_net.py:700 #, python-format msgid "Pid %d is stale, relaunching dnsmasq" msgstr "Pid %d está ultrapassado, reiniciando dnsmasq" #. pylint: disable=W0703 -#: ../nova/network/linux_net.py:731 +#: ../nova/network/linux_net.py:760 #, python-format msgid "killing radvd threw %s" msgstr "" -#: ../nova/network/linux_net.py:733 +#: ../nova/network/linux_net.py:762 #, python-format msgid "Pid %d is stale, relaunching radvd" msgstr "Pid %d está ultrapassado, reiniciando radvd" @@ -635,7 +635,7 @@ msgstr "Classe %s não pode ser encontrada" #: ../nova/utils.py:150 #, python-format msgid "Fetching %s" -msgstr "Obtendo %s" +msgstr "Buscando %s" #: ../nova/utils.py:201 #, python-format @@ -650,35 +650,35 @@ msgstr "Resultado foi %s" #: ../nova/utils.py:273 #, python-format msgid "Running cmd (SSH): %s" -msgstr "Rodando o comando (SSH): %s" +msgstr "Executando o comando (SSH): %s" #: ../nova/utils.py:337 #, python-format msgid "debug in callback: %s" -msgstr "debug em callback: %s" +msgstr "depuração em retorno de chamada: %s" #: ../nova/utils.py:342 #, python-format msgid "Running %s" msgstr "Executando %s" -#: ../nova/utils.py:470 +#: ../nova/utils.py:480 #, python-format msgid "Link Local address is not found.:%s" msgstr "Endereço para Link Local não encontrado: %s" -#: ../nova/utils.py:473 +#: ../nova/utils.py:483 #, python-format msgid "Couldn't get Link Local IP of %(interface)s :%(ex)s" msgstr "" "Não foi possível atribuir um IP para o Link Local de %(interface)s :%(ex)s" -#: ../nova/utils.py:570 +#: ../nova/utils.py:580 #, python-format msgid "Invalid backend: %s" msgstr "Backend inválido: %s" -#: ../nova/utils.py:581 +#: ../nova/utils.py:591 #, python-format msgid "backend %s" msgstr "backend %s" @@ -748,12 +748,12 @@ msgstr "Não é possível desconectar o VBD %s" msgid "Unable to destroy VBD %s" msgstr "Não é possível destruir o VBD %s" -#: ../nova/virt/xenapi/vmops.py:1460 +#: ../nova/virt/xenapi/vmops.py:1489 #, python-format msgid "Creating VIF for VM %(vm_ref)s, network %(network_ref)s." msgstr "Criando a VIF para VM %(vm_ref)s, rede %(network_ref)s." -#: ../nova/virt/xenapi/vmops.py:1463 +#: ../nova/virt/xenapi/vmops.py:1492 #, python-format msgid "Created VIF %(vif_ref)s for VM %(vm_ref)s, network %(network_ref)s." msgstr "VIF %(vif_ref)s criada para VM %(vm_ref)s, rede %(network_ref)s." @@ -799,12 +799,12 @@ msgstr "Visão geral da imagem %s" #. We need to invoke a plugin for copying the #. content of the VDI into the proper path. -#: ../nova/virt/xenapi/vm_utils.py:668 +#: ../nova/virt/xenapi/vm_utils.py:689 #, python-format msgid "Copying VDI %s to /boot/guest on dom0" msgstr "Copiando o VDI %s de /boot/guest no dom0" -#: ../nova/virt/xenapi/vm_utils.py:678 +#: ../nova/virt/xenapi/vm_utils.py:699 #, python-format msgid "Kernel/Ramdisk VDI %s destroyed" msgstr "Kernel/Ramdisk %s destruidos" @@ -814,7 +814,7 @@ msgstr "Kernel/Ramdisk %s destruidos" msgid "Asking xapi to fetch %(url)s as %(access)s" msgstr "Requisitando à xapi a busca da url %(url)s como %(access)s" -#: ../nova/virt/xenapi/vm_utils.py:756 +#: ../nova/virt/xenapi/vm_utils.py:770 #, python-format msgid "Looking up vdi %s for PV kernel" msgstr "Verificando o vdi %s para kernel PV" @@ -824,17 +824,17 @@ msgstr "Verificando o vdi %s para kernel PV" msgid "PV Kernel in VDI:%s" msgstr "Kernel PV no VDI: %s" -#: ../nova/virt/xenapi/vm_utils.py:1299 +#: ../nova/virt/xenapi/vm_utils.py:1313 #, python-format msgid "Running pygrub against %s" msgstr "Rodando pygrub novamente %s" -#: ../nova/virt/xenapi/vm_utils.py:1306 +#: ../nova/virt/xenapi/vm_utils.py:1320 #, python-format msgid "Found Xen kernel %s" msgstr "Kernel Xen encontrado: %s" -#: ../nova/virt/xenapi/vm_utils.py:1308 +#: ../nova/virt/xenapi/vm_utils.py:1322 msgid "No Xen kernel found. Booting HVM." msgstr "Kernel Xen não encontrado. Iniciando como HVM." @@ -843,38 +843,38 @@ msgstr "Kernel Xen não encontrado. Iniciando como HVM." msgid "duplicate name found: %s" msgstr "nome duplicado encontrado: %s" -#: ../nova/virt/xenapi/vm_utils.py:816 +#: ../nova/virt/xenapi/vm_utils.py:830 #, python-format msgid "VDI %s is still available" msgstr "O VDI %s continua disponível" -#: ../nova/virt/xenapi/vm_utils.py:862 +#: ../nova/virt/xenapi/vm_utils.py:876 #, python-format msgid "(VM_UTILS) xenserver vm state -> |%s|" msgstr "(VM_UTILS) xenserver vm state -> |%s|" -#: ../nova/virt/xenapi/vm_utils.py:864 +#: ../nova/virt/xenapi/vm_utils.py:878 #, python-format msgid "(VM_UTILS) xenapi power_state -> |%s|" msgstr "(VM_UTILS) xenapi power_state -> |%s|" -#: ../nova/virt/xenapi/vm_utils.py:1106 +#: ../nova/virt/xenapi/vm_utils.py:1120 #, python-format msgid "VHD %(vdi_uuid)s has parent %(parent_ref)s" msgstr "O VHD %(vdi_uuid)s tem pai %(parent_ref)s" -#: ../nova/virt/xenapi/vm_utils.py:920 +#: ../nova/virt/xenapi/vm_utils.py:934 #, python-format msgid "Re-scanning SR %s" msgstr "Re-escaneando SR %s" -#: ../nova/virt/xenapi/vm_utils.py:1157 +#: ../nova/virt/xenapi/vm_utils.py:1171 #, python-format msgid "" "VHD coalesce attempts exceeded (%(counter)d > %(max_attempts)d), giving up..." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:1164 +#: ../nova/virt/xenapi/vm_utils.py:1178 #, python-format msgid "" "Parent %(parent_uuid)s doesn't match original parent " @@ -892,25 +892,25 @@ msgid "Unexpected number of VDIs (%(num_vdis)s) found for VM %(vm_ref)s" msgstr "" "Número de VDIs inesperado (%(num_vdis)s) encontrado para MV %(vm_ref)s" -#: ../nova/virt/xenapi/vm_utils.py:1226 +#: ../nova/virt/xenapi/vm_utils.py:1240 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:188 #, python-format msgid "Creating VBD for VDI %s ... " msgstr "Criando VBD para VDI %s ... " -#: ../nova/virt/xenapi/vm_utils.py:1228 +#: ../nova/virt/xenapi/vm_utils.py:1242 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:190 #, python-format msgid "Creating VBD for VDI %s done." msgstr "O VBD para VDI %s foi criado." -#: ../nova/virt/xenapi/vm_utils.py:1230 +#: ../nova/virt/xenapi/vm_utils.py:1244 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:192 #, python-format msgid "Plugging VBD %s ... " msgstr "Conectando VBD %s ... " -#: ../nova/virt/xenapi/vm_utils.py:1233 +#: ../nova/virt/xenapi/vm_utils.py:1247 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:194 #, python-format msgid "Plugging VBD %s done." @@ -926,40 +926,40 @@ msgstr "VBD %(vbd)s conectado como %(orig_dev)s" msgid "VBD %(vbd)s plugged into wrong dev, remapping to %(dev)s" msgstr "VBD %(vbd)s conectado no device errado, remapeando para %(dev)s" -#: ../nova/virt/xenapi/vm_utils.py:1247 +#: ../nova/virt/xenapi/vm_utils.py:1261 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:197 #, python-format msgid "Destroying VBD for VDI %s ... " msgstr "Destruindo VBD para o VDI %s ... " -#: ../nova/virt/xenapi/vm_utils.py:1251 +#: ../nova/virt/xenapi/vm_utils.py:1265 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:200 #, python-format msgid "Destroying VBD for VDI %s done." msgstr "O VBD para o VDI %s foi destruído." -#: ../nova/virt/xenapi/vm_utils.py:1263 +#: ../nova/virt/xenapi/vm_utils.py:1277 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:211 msgid "VBD.unplug successful first time." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:1268 +#: ../nova/virt/xenapi/vm_utils.py:1282 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:216 msgid "VBD.unplug rejected: retrying..." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:1273 +#: ../nova/virt/xenapi/vm_utils.py:1287 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:220 msgid "VBD.unplug successful eventually." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:1276 +#: ../nova/virt/xenapi/vm_utils.py:1290 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:223 #, python-format msgid "Ignoring XenAPI.Failure in VBD.unplug: %s" msgstr "Ignorando XenAPI.Failure em VBD.unplug: %s" -#: ../nova/virt/xenapi/vm_utils.py:1285 +#: ../nova/virt/xenapi/vm_utils.py:1299 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:66 #, python-format msgid "Ignoring XenAPI.Failure %s" @@ -971,7 +971,7 @@ msgid "" "Writing partition table %(primary_first)d %(primary_last)d to %(dest)s..." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:1366 +#: ../nova/virt/xenapi/vm_utils.py:1380 #, python-format msgid "Writing partition table %s done." msgstr "" @@ -991,7 +991,7 @@ msgstr "" msgid "Received %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:52 +#: ../nova/db/sqlalchemy/api.py:56 msgid "Use of empty request context is deprecated" msgstr "" @@ -1131,26 +1131,26 @@ msgstr "" msgid "No zone with id %(zone_id)s" msgstr "" -#: ../nova/compute/manager.py:221 +#: ../nova/compute/manager.py:223 #, python-format msgid "Checking state of %s" -msgstr "" +msgstr "Verificando o estado de %s" #: ../nova/virt/libvirt_conn.py:165 #, python-format msgid "Current state of %(name)s was %(state)s." msgstr "" -#: ../nova/virt/libvirt/connection.py:228 +#: ../nova/virt/libvirt/connection.py:225 #, python-format msgid "Connecting to libvirt: %s" msgstr "" -#: ../nova/virt/libvirt/connection.py:242 +#: ../nova/virt/libvirt/connection.py:239 msgid "Connection to libvirt broke" msgstr "" -#: ../nova/virt/libvirt/connection.py:403 +#: ../nova/virt/libvirt/connection.py:401 #, python-format msgid "instance %(instance_name)s: deleting instance files %(target)s" msgstr "" @@ -1189,7 +1189,7 @@ msgstr "" msgid "_wait_for_rescue failed: %s" msgstr "" -#: ../nova/virt/libvirt/connection.py:680 +#: ../nova/virt/libvirt/connection.py:678 #, python-format msgid "instance %s: is running" msgstr "" @@ -1204,16 +1204,16 @@ msgstr "" msgid "instance %s: failed to boot" msgstr "" -#: ../nova/virt/libvirt/connection.py:703 +#: ../nova/virt/libvirt/connection.py:701 #, python-format msgid "virsh said: %r" msgstr "" -#: ../nova/virt/libvirt/connection.py:707 +#: ../nova/virt/libvirt/connection.py:705 msgid "cool, it's a device" msgstr "" -#: ../nova/virt/libvirt/connection.py:718 +#: ../nova/virt/libvirt/connection.py:716 #, python-format msgid "data: %(data)r, fpath: %(fpath)r" msgstr "" @@ -1227,7 +1227,7 @@ msgstr "" msgid "Unable to find an open port" msgstr "Impossível localizar uma porta aberta" -#: ../nova/virt/libvirt/connection.py:868 +#: ../nova/virt/libvirt/connection.py:865 #, python-format msgid "instance %s: Creating image" msgstr "" @@ -1243,7 +1243,7 @@ msgid "instance %(inst_name)s: injecting net into image %(img_id)s" msgstr "" #. This could be a windows image, or a vmdk format disk -#: ../nova/virt/libvirt/connection.py:1058 +#: ../nova/virt/libvirt/connection.py:1057 #, python-format msgid "" "instance %(inst_name)s: ignoring error injecting data into image %(img_id)s " @@ -1265,12 +1265,12 @@ msgstr "" msgid "diagnostics are not supported for libvirt" msgstr "" -#: ../nova/virt/libvirt/firewall.py:486 ../nova/virt/firewall.py:125 +#: ../nova/virt/firewall.py:125 ../nova/virt/libvirt/firewall.py:486 #, python-format msgid "Attempted to unfilter instance %s which is not filtered" msgstr "" -#: ../nova/api/metadata/handler.py:251 ../nova/api/metadata/handler.py:258 +#: ../nova/api/metadata/handler.py:252 ../nova/api/metadata/handler.py:259 #, python-format msgid "Failed to get metadata for ip: %s" msgstr "Falha ao obter metadados para o ip: %s" @@ -1284,11 +1284,11 @@ msgstr "Tentativa de instanciar singleton" msgid "Quota exceeeded for %s, tried to allocate address" msgstr "" -#: ../nova/network/manager.py:302 +#: ../nova/network/manager.py:338 msgid "Address quota exceeded. You cannot allocate any more addresses" msgstr "" -#: ../nova/tests/test_volume.py:190 +#: ../nova/tests/test_volume.py:195 #, python-format msgid "Target %s allocated" msgstr "" @@ -1298,15 +1298,15 @@ msgstr "" msgid "Finished retreving %(url)s -- placed in %(path)s" msgstr "" -#: ../nova/scheduler/driver.py:169 +#: ../nova/scheduler/driver.py:190 msgid "Must implement a fallback schedule" msgstr "" -#: ../nova/console/manager.py:71 ../nova/console/vmrc_manager.py:64 +#: ../nova/console/manager.py:70 ../nova/console/vmrc_manager.py:64 msgid "Adding console" -msgstr "" +msgstr "Adicionando console" -#: ../nova/console/manager.py:91 +#: ../nova/console/manager.py:90 #, python-format msgid "Tried to remove non-existant console %(console_id)s." msgstr "" @@ -1320,19 +1320,17 @@ msgstr "" msgid "The key_pair %s already exists" msgstr "" -#. TODO(vish): Do this with M2Crypto instead -#: ../nova/api/ec2/cloud.py:237 -#: ../nova/api/openstack/v2/contrib/cloudpipe.py:82 +#: ../nova/crypto.py:112 #, python-format msgid "Generating root CA: %s" msgstr "Gerando CA raiz: %s" -#: ../nova/api/ec2/cloud.py:395 +#: ../nova/api/ec2/cloud.py:368 #, python-format msgid "Create key pair %s" msgstr "Criar par de chaves %s" -#: ../nova/api/ec2/cloud.py:437 +#: ../nova/api/ec2/cloud.py:395 #, python-format msgid "Delete key pair %s" msgstr "Remover par de chaves %s" @@ -1346,103 +1344,103 @@ msgstr "%s não é um ipProtocol válido" msgid "Invalid port range" msgstr "Intervalo de porta inválido" -#: ../nova/api/openstack/v2/contrib/security_groups.py:490 +#: ../nova/api/openstack/compute/contrib/security_groups.py:497 #, python-format msgid "Revoke security group ingress %s" msgstr "Revogado entrada do grupo de segurança %s" -#: ../nova/api/openstack/v2/contrib/security_groups.py:352 +#: ../nova/api/openstack/compute/contrib/security_groups.py:358 msgid "Not enough parameters to build a valid rule." msgstr "" -#: ../nova/api/ec2/cloud.py:696 ../nova/api/ec2/cloud.py:752 +#: ../nova/api/ec2/cloud.py:654 ../nova/api/ec2/cloud.py:710 msgid "No rule for the specified parameters." msgstr "Não existe regra para os parâmetros especificados" -#: ../nova/api/openstack/v2/contrib/security_groups.py:337 +#: ../nova/api/openstack/compute/contrib/security_groups.py:343 #, python-format msgid "Authorize security group ingress %s" msgstr "Autorizada entrada do grupo de segurança %s" -#: ../nova/api/openstack/v2/contrib/security_groups.py:359 +#: ../nova/api/openstack/compute/contrib/security_groups.py:365 #, python-format msgid "This rule already exists in group %s" msgstr "Esta regra já existe no grupo %s" -#: ../nova/api/ec2/cloud.py:788 -#: ../nova/api/openstack/v2/contrib/security_groups.py:277 +#: ../nova/api/ec2/cloud.py:746 +#: ../nova/api/openstack/compute/contrib/security_groups.py:282 #, python-format msgid "Create Security Group %s" msgstr "Criar Grupo de Segurança %s" -#: ../nova/api/ec2/cloud.py:791 +#: ../nova/api/ec2/cloud.py:749 #, python-format msgid "group %s already exists" msgstr "group %s já existe" -#: ../nova/api/ec2/cloud.py:818 -#: ../nova/api/openstack/v2/contrib/security_groups.py:234 +#: ../nova/api/ec2/cloud.py:776 +#: ../nova/api/openstack/compute/contrib/security_groups.py:237 #, python-format msgid "Delete security group %s" msgstr "Excluir grupo de segurança %s" -#: ../nova/api/openstack/v2/contrib/volumes.py:182 -#: ../nova/api/ec2/cloud.py:915 +#: ../nova/api/openstack/compute/contrib/volumes.py:186 +#: ../nova/api/openstack/volume/volumes.py:215 ../nova/api/ec2/cloud.py:865 #, python-format msgid "Create volume of %s GB" msgstr "Criar volume de %s GB" -#: ../nova/api/ec2/cloud.py:950 +#: ../nova/api/ec2/cloud.py:899 #, python-format msgid "Attach volume %(volume_id)s to instance %(instance_id)s at %(device)s" msgstr "" -#: ../nova/api/openstack/v2/contrib/volumes.py:347 -#: ../nova/api/ec2/cloud.py:964 +#: ../nova/api/openstack/compute/contrib/volumes.py:361 +#: ../nova/api/ec2/cloud.py:913 #, python-format msgid "Detach volume %s" msgstr "Desanexar volume %s" -#: ../nova/api/ec2/cloud.py:1244 +#: ../nova/api/ec2/cloud.py:1190 msgid "Allocate address" msgstr "Alocar endereço" -#: ../nova/api/ec2/cloud.py:1256 +#: ../nova/api/ec2/cloud.py:1202 #, python-format msgid "Release address %s" msgstr "Liberar endereço %s" -#: ../nova/api/ec2/cloud.py:1261 +#: ../nova/api/ec2/cloud.py:1207 #, python-format msgid "Associate address %(public_ip)s to instance %(instance_id)s" msgstr "" -#: ../nova/api/ec2/cloud.py:1271 +#: ../nova/api/ec2/cloud.py:1217 #, python-format msgid "Disassociate address %s" msgstr "Desatribuir endereço %s" -#: ../nova/api/ec2/cloud.py:1319 +#: ../nova/api/ec2/cloud.py:1265 msgid "Going to start terminating instances" msgstr "Começando a terminar instâncias" -#: ../nova/api/ec2/cloud.py:1328 +#: ../nova/api/ec2/cloud.py:1274 #, python-format msgid "Reboot instance %r" msgstr "Reiniciar instância %r" -#: ../nova/api/ec2/cloud.py:1469 +#: ../nova/api/ec2/cloud.py:1415 #, python-format msgid "De-registering image %s" msgstr "Removendo o registro da imagem %s" -#: ../nova/api/ec2/cloud.py:1496 +#: ../nova/api/ec2/cloud.py:1442 #, python-format msgid "Registered image %(image_location)s with id %(image_id)s" msgstr "" -#: ../nova/api/ec2/cloud.py:991 ../nova/api/ec2/cloud.py:1048 -#: ../nova/api/ec2/cloud.py:1524 ../nova/api/ec2/cloud.py:1539 +#: ../nova/api/ec2/cloud.py:940 ../nova/api/ec2/cloud.py:997 +#: ../nova/api/ec2/cloud.py:1470 ../nova/api/ec2/cloud.py:1485 #, python-format msgid "attribute not supported: %s" msgstr "atributo não suportado: %s" @@ -1452,19 +1450,19 @@ msgstr "atributo não suportado: %s" msgid "invalid id: %s" msgstr "id inválido: %s" -#: ../nova/api/ec2/cloud.py:1542 +#: ../nova/api/ec2/cloud.py:1488 msgid "user or group not specified" msgstr "usuário ou grupo não especificado" -#: ../nova/api/ec2/cloud.py:1544 +#: ../nova/api/ec2/cloud.py:1490 msgid "only group \"all\" is supported" msgstr "apenas o grupo \"all\" é suportado" -#: ../nova/api/ec2/cloud.py:1546 +#: ../nova/api/ec2/cloud.py:1492 msgid "operation_type must be add or remove" msgstr "operation_type deve ser add ou remove" -#: ../nova/api/ec2/cloud.py:1547 +#: ../nova/api/ec2/cloud.py:1493 #, python-format msgid "Updating image %s publicity" msgstr "Atualizando publicidade da imagem %s" @@ -1550,12 +1548,12 @@ msgstr "" msgid "instance %(name)s: not enough free memory" msgstr "" -#: ../nova/virt/xenapi/vmops.py:441 +#: ../nova/virt/xenapi/vmops.py:449 #, python-format msgid "Starting VM %s..." msgstr "" -#: ../nova/virt/xenapi/vmops.py:444 +#: ../nova/virt/xenapi/vmops.py:452 #, python-format msgid "Spawning VM %(instance_name)s created %(vm_ref)s." msgstr "" @@ -1565,7 +1563,7 @@ msgstr "" msgid "Invalid value for onset_files: '%s'" msgstr "" -#: ../nova/virt/xenapi/vmops.py:418 +#: ../nova/virt/xenapi/vmops.py:426 #, python-format msgid "Injecting file path: '%s'" msgstr "" @@ -1582,17 +1580,17 @@ msgstr "" #. TODO(sirp): Add quiesce and VSS locking support when Windows support #. is added -#: ../nova/virt/xenapi/vmops.py:614 +#: ../nova/virt/xenapi/vmops.py:622 #, python-format msgid "Starting snapshot for VM %s" msgstr "" -#: ../nova/virt/xenapi/vmops.py:623 +#: ../nova/virt/xenapi/vmops.py:631 #, python-format msgid "Unable to Snapshot %(vm_ref)s: %(exc)s" msgstr "" -#: ../nova/virt/xenapi/vmops.py:608 +#: ../nova/virt/xenapi/vmops.py:616 #, python-format msgid "Finished snapshot and upload for VM %s" msgstr "" @@ -1606,7 +1604,7 @@ msgstr "" msgid "Removing kernel/ramdisk files" msgstr "" -#: ../nova/virt/xenapi/vmops.py:1091 +#: ../nova/virt/xenapi/vmops.py:1109 msgid "kernel/ramdisk files removed" msgstr "" @@ -1631,17 +1629,17 @@ msgid "" "args=%(strargs)s" msgstr "" -#: ../nova/virt/xenapi/vmops.py:1788 +#: ../nova/virt/xenapi/vmops.py:1817 #, python-format msgid "OpenSSL error: %s" -msgstr "" +msgstr "Erro de OpenSSL: %s" -#: ../nova/tests/test_compute.py:335 ../nova/tests/test_compute.py:1307 +#: ../nova/tests/test_compute.py:339 ../nova/tests/test_compute.py:1368 #, python-format msgid "Running instances: %s" msgstr "" -#: ../nova/tests/test_compute.py:341 +#: ../nova/tests/test_compute.py:345 #, python-format msgid "After terminating instances: %s" msgstr "" @@ -1672,69 +1670,69 @@ msgstr "" msgid "Image %s could not be found" msgstr "" -#: ../nova/api/ec2/__init__.py:134 +#: ../nova/api/ec2/__init__.py:140 msgid "Too many failed authentications." msgstr "Muitas falhas de autenticação." -#: ../nova/api/ec2/__init__.py:144 +#: ../nova/api/ec2/__init__.py:150 #, python-format msgid "" "Access key %(access_key)s has had %(failures)d failed authentications and " "will be locked out for %(lock_mins)d minutes." msgstr "" -#: ../nova/api/ec2/__init__.py:204 +#: ../nova/api/ec2/__init__.py:267 #, python-format msgid "Authentication Failure: %s" msgstr "Falha de Autenticação: %s" -#: ../nova/api/ec2/__init__.py:220 +#: ../nova/api/ec2/__init__.py:283 #, python-format msgid "Authenticated Request For %(uname)s:%(pname)s)" msgstr "" -#: ../nova/api/ec2/__init__.py:251 +#: ../nova/api/ec2/__init__.py:314 #, python-format msgid "action: %s" msgstr "ação: %s" -#: ../nova/api/ec2/__init__.py:253 +#: ../nova/api/ec2/__init__.py:316 #, python-format msgid "arg: %(key)s\t\tval: %(value)s" msgstr "" -#: ../nova/api/ec2/__init__.py:328 +#: ../nova/api/ec2/__init__.py:391 #, python-format msgid "" "Unauthorized request for controller=%(controller)s and action=%(action)s" msgstr "" -#: ../nova/api/ec2/__init__.py:359 +#: ../nova/api/ec2/__init__.py:462 #, python-format msgid "InstanceNotFound raised: %s" msgstr "" -#: ../nova/api/ec2/__init__.py:365 +#: ../nova/api/ec2/__init__.py:468 #, python-format msgid "VolumeNotFound raised: %s" msgstr "" -#: ../nova/api/ec2/__init__.py:377 +#: ../nova/api/ec2/__init__.py:480 #, python-format msgid "NotFound raised: %s" msgstr "NotFound lançado: %s" -#: ../nova/api/ec2/__init__.py:380 +#: ../nova/api/ec2/__init__.py:483 #, python-format msgid "ApiError raised: %s" msgstr "ApiError lançado: %s" -#: ../nova/api/ec2/__init__.py:409 +#: ../nova/api/ec2/__init__.py:512 #, python-format msgid "Unexpected error raised: %s" msgstr "Erro inexperado lançado: %s" -#: ../nova/api/metadata/handler.py:253 ../nova/api/ec2/__init__.py:414 +#: ../nova/api/metadata/handler.py:254 ../nova/api/ec2/__init__.py:517 msgid "An unknown error has occurred. Please try your request again." msgstr "" "Ocorreu um erro desconhecido. Por favor tente sua requisição novamente." @@ -1780,17 +1778,17 @@ msgid "" "and xenapi_connection_password to use connection_type=xenapi" msgstr "" -#: ../nova/virt/xenapi_conn.py:560 +#: ../nova/virt/xenapi_conn.py:569 #, python-format msgid "Task [%(name)s] %(task)s status: success %(result)s" msgstr "" -#: ../nova/virt/xenapi_conn.py:569 +#: ../nova/virt/xenapi_conn.py:578 #, python-format msgid "Task [%(name)s] %(task)s status: %(status)s %(error_info)s" msgstr "" -#: ../nova/virt/xenapi_conn.py:595 ../nova/virt/xenapi_conn.py:608 +#: ../nova/virt/xenapi_conn.py:604 ../nova/virt/xenapi_conn.py:617 #, python-format msgid "Got exception: %s" msgstr "" @@ -1806,7 +1804,7 @@ msgid "" "Unsupported API request: controller = %(controller)s, action = %(action)s" msgstr "" -#: ../nova/api/openstack/v2/__init__.py:61 +#: ../nova/api/openstack/__init__.py:43 #, python-format msgid "Caught error: %s" msgstr "Capturado o erro: %s" @@ -1815,33 +1813,33 @@ msgstr "Capturado o erro: %s" msgid "Including admin operations in API." msgstr "Incluindo operações administrativas na API." -#: ../nova/console/xvp.py:93 +#: ../nova/console/xvp.py:92 msgid "Rebuilding xvp conf" msgstr "" -#: ../nova/console/xvp.py:111 +#: ../nova/console/xvp.py:110 #, python-format msgid "Re-wrote %s" msgstr "" -#: ../nova/console/xvp.py:116 +#: ../nova/console/xvp.py:115 msgid "Stopping xvp" msgstr "" -#: ../nova/console/xvp.py:129 +#: ../nova/console/xvp.py:128 msgid "Starting xvp" msgstr "" -#: ../nova/console/xvp.py:136 +#: ../nova/console/xvp.py:135 #, python-format msgid "Error starting xvp: %s" msgstr "" -#: ../nova/console/xvp.py:139 +#: ../nova/console/xvp.py:138 msgid "Restarting xvp" msgstr "" -#: ../nova/console/xvp.py:141 +#: ../nova/console/xvp.py:140 msgid "xvp not running..." msgstr "" @@ -1867,11 +1865,11 @@ msgid "" "Please create a database by running a nova-api server on this host." msgstr "" -#: ../bin/nova-manage.py:640 +#: ../bin/nova-manage.py:645 msgid "network" msgstr "" -#: ../bin/nova-manage.py:641 +#: ../bin/nova-manage.py:646 msgid "IP address" msgstr "" @@ -1879,12 +1877,12 @@ msgstr "" msgid "MAC address" msgstr "" -#: ../bin/nova-manage.py:643 +#: ../bin/nova-manage.py:647 msgid "hostname" msgstr "" -#: ../bin/nova-manage.py:644 ../bin/nova-manage.py:1181 -#: ../bin/nova-manage.py:1297 ../bin/nova-manage.py:1329 +#: ../bin/nova-manage.py:648 ../bin/nova-manage.py:1166 +#: ../bin/nova-manage.py:1282 ../bin/nova-manage.py:1314 msgid "host" msgstr "" @@ -1892,7 +1890,7 @@ msgstr "" msgid "netmask" msgstr "" -#: ../bin/nova-manage.py:852 ../nova/tests/test_nova_manage.py:183 +#: ../bin/nova-manage.py:857 ../nova/tests/test_nova_manage.py:181 msgid "start address" msgstr "" @@ -1901,7 +1899,7 @@ msgstr "" msgid "Failed to load partition: %s" msgstr "" -#: ../nova/virt/disk/api.py:222 ../nova/virt/disk/guestfs.py:64 +#: ../nova/virt/disk/api.py:223 ../nova/virt/disk/guestfs.py:64 #: ../nova/virt/disk/guestfs.py:78 ../nova/virt/disk/mount.py:112 #, python-format msgid "Failed to mount filesystem: %s" @@ -1922,7 +1920,7 @@ msgstr "" msgid "No free nbd devices" msgstr "" -#: ../doc/ext/nova_todo.py:46 +#: ../doc/ext/nova_todo.py:45 #, python-format msgid "%(filename)s, line %(line_info)d" msgstr "" @@ -2078,17 +2076,17 @@ msgstr "" msgid "Quota exceeeded for %(pid)s, tried to run %(min_count)s instances" msgstr "" -#: ../nova/compute/api.py:219 +#: ../nova/compute/api.py:215 #, python-format msgid "" "Instance quota exceeded. You can only run %s more instances of this type." msgstr "" -#: ../nova/compute/api.py:268 +#: ../nova/compute/api.py:264 msgid "Creating a raw instance" msgstr "" -#: ../nova/compute/api.py:323 +#: ../nova/compute/api.py:336 #, python-format msgid "Going to run %s instances..." msgstr "" @@ -2098,7 +2096,7 @@ msgstr "" msgid "Casting to scheduler for %(pid)s/%(uid)s's instance %(instance_id)s" msgstr "" -#: ../nova/compute/api.py:852 +#: ../nova/compute/api.py:872 #, python-format msgid "Going to try to terminate %s" msgstr "" @@ -2113,7 +2111,7 @@ msgstr "" msgid "Instance %d is already being terminated" msgstr "" -#: ../nova/compute/api.py:1644 +#: ../nova/compute/api.py:1643 #, python-format msgid "Invalid device specified: %s. Example device: /dev/vdb" msgstr "" @@ -2135,7 +2133,7 @@ msgid "Unable to connect to AMQP server after %d tries. Shutting down." msgstr "" "Não foi possível conectar ao servidor AMQP após %d tentativas. Desligando." -#: ../nova/rpc/impl_carrot.py:221 +#: ../nova/rpc/impl_carrot.py:222 msgid "Reconnected to queue" msgstr "Reconectado à fila" @@ -2143,12 +2141,12 @@ msgstr "Reconectado à fila" msgid "Failed to fetch message from queue" msgstr "Falha ao obter mensagem da fila" -#: ../nova/rpc/impl_carrot.py:236 +#: ../nova/rpc/impl_carrot.py:237 #, python-format msgid "Initing the Adapter Consumer for %s" msgstr "Iniciando o Adaptador Consumidor para %s" -#: ../nova/rpc/impl_kombu.py:629 ../nova/rpc/impl_carrot.py:256 +#: ../nova/rpc/amqp.py:224 ../nova/rpc/impl_carrot.py:261 #, python-format msgid "received %s" msgstr "recebido %s" @@ -2157,22 +2155,22 @@ msgstr "recebido %s" #. messages stay in the queue indefinitely, so for now #. we just log the message and send an error string #. back to the caller -#: ../nova/rpc/impl_kombu.py:634 ../nova/rpc/impl_carrot.py:269 +#: ../nova/rpc/amqp.py:229 ../nova/rpc/impl_carrot.py:274 #, python-format msgid "no method for message: %s" msgstr "sem método para mensagem: %s" -#: ../nova/rpc/impl_kombu.py:635 ../nova/rpc/impl_carrot.py:271 +#: ../nova/rpc/amqp.py:230 ../nova/rpc/impl_carrot.py:276 #, python-format msgid "No method for message: %s" msgstr "Sem método para mensagem: %s" -#: ../nova/rpc/impl_kombu.py:816 ../nova/rpc/impl_carrot.py:455 +#: ../nova/rpc/amqp.py:141 ../nova/rpc/impl_carrot.py:460 #, python-format msgid "Returning exception %s to caller" msgstr "Retornando exceção %s ao método de origem" -#: ../nova/rpc/impl_kombu.py:675 ../nova/rpc/impl_carrot.py:488 +#: ../nova/rpc/amqp.py:182 ../nova/rpc/impl_carrot.py:494 #, python-format msgid "unpacked context: %s" msgstr "conteúdo descompactado: %s" @@ -2181,7 +2179,7 @@ msgstr "conteúdo descompactado: %s" msgid "Making asynchronous call..." msgstr "Fazendo chamada assíncrona..." -#: ../nova/rpc/impl_kombu.py:762 ../nova/rpc/impl_carrot.py:524 +#: ../nova/rpc/amqp.py:314 ../nova/rpc/impl_carrot.py:530 #, python-format msgid "MSG_ID is %s" msgstr "MSG_ID é %s" @@ -2190,17 +2188,17 @@ msgstr "MSG_ID é %s" msgid "Making asynchronous cast..." msgstr "" -#: ../nova/rpc/impl_carrot.py:634 +#: ../nova/rpc/impl_carrot.py:640 #, python-format msgid "response %s" msgstr "resposta %s" -#: ../nova/rpc/impl_carrot.py:643 +#: ../nova/rpc/impl_carrot.py:649 #, python-format msgid "topic is %s" msgstr "topico é %s" -#: ../nova/rpc/impl_carrot.py:644 +#: ../nova/rpc/impl_carrot.py:650 #, python-format msgid "message %s" msgstr "mensagem %s" @@ -2253,7 +2251,7 @@ msgstr "" msgid "Starting %(arg0)s on %(host)s:%(port)s" msgstr "" -#: ../nova/wsgi.py:204 +#: ../nova/wsgi.py:207 msgid "You must implement __call__" msgstr "" @@ -2286,7 +2284,7 @@ msgstr "" msgid "Dissassociated %s stale fixed ip(s)" msgstr "" -#: ../nova/network/manager.py:564 +#: ../nova/network/manager.py:705 msgid "setting network host" msgstr "" @@ -2325,12 +2323,12 @@ msgstr "" msgid "IP %(address)s released from bad mac %(inst_addr)s vs %(mac)s" msgstr "" -#: ../nova/network/manager.py:1063 +#: ../nova/network/manager.py:1243 #, python-format msgid "IP %s released that was not leased" msgstr "" -#: ../nova/network/manager.py:1490 +#: ../nova/network/manager.py:1692 msgid "" "The sum between the number of networks and the vlan start cannot be greater " "than 4094" @@ -2339,7 +2337,7 @@ msgstr "" #: ../nova/virt/xenapi/volume_utils.py:157 #, python-format msgid "Introducing %s..." -msgstr "" +msgstr "Introduzindo %s..." #: ../nova/virt/xenapi/volume_utils.py:103 #: ../nova/virt/xenapi/volume_utils.py:170 @@ -2406,19 +2404,19 @@ msgstr "" #: ../nova/virt/xenapi/volume_utils.py:356 #, python-format msgid "Mountpoint cannot be translated: %s" -msgstr "" +msgstr "Ponto de montagem não pode ser traduzido: %s" -#: ../nova/image/s3.py:365 +#: ../nova/image/s3.py:361 #, python-format msgid "Failed to decrypt private key: %s" msgstr "" -#: ../nova/image/s3.py:374 +#: ../nova/image/s3.py:369 #, python-format msgid "Failed to decrypt initialization vector: %s" msgstr "" -#: ../nova/image/s3.py:385 +#: ../nova/image/s3.py:380 #, python-format msgid "Failed to decrypt image file %(image_file)s: %(err)s" msgstr "" @@ -2532,12 +2530,12 @@ msgstr "" msgid "Deleted image: %s" msgstr "" -#: ../nova/auth/manager.py:277 +#: ../nova/auth/manager.py:280 #, python-format msgid "Looking up user: %r" msgstr "Procurando usuário: %r" -#: ../nova/auth/manager.py:281 +#: ../nova/auth/manager.py:284 #, python-format msgid "Failed authorization for access key %s" msgstr "Falha de autorização para chave de acesso %s" @@ -2547,12 +2545,12 @@ msgstr "Falha de autorização para chave de acesso %s" msgid "No user found for access key %s" msgstr "Nenhum usuário encontrado para chave de acesso %s" -#: ../nova/auth/manager.py:287 +#: ../nova/auth/manager.py:290 #, python-format msgid "Using project name = user name (%s)" msgstr "Usando nome do projeto = nome do usuário (%s)" -#: ../nova/auth/manager.py:294 +#: ../nova/auth/manager.py:297 #, python-format msgid "failed authorization: no project named %(pjid)s (user=%(uname)s)" msgstr "" @@ -2562,7 +2560,7 @@ msgstr "" msgid "No project called %s could be found" msgstr "Nenhum projeto chamado %s pode ser encontrado." -#: ../nova/auth/manager.py:303 +#: ../nova/auth/manager.py:306 #, python-format msgid "" "Failed authorization: user %(uname)s not admin and not member of project " @@ -2574,7 +2572,7 @@ msgstr "" msgid "User %(uid)s is not a member of project %(pjid)s" msgstr "" -#: ../nova/auth/manager.py:314 ../nova/auth/manager.py:336 +#: ../nova/auth/manager.py:317 ../nova/auth/manager.py:339 #, python-format msgid "Invalid signature for user %s" msgstr "Assinatura inválida para usuário %s" @@ -2583,7 +2581,7 @@ msgstr "Assinatura inválida para usuário %s" msgid "Signature does not match" msgstr "Assinatura não confere" -#: ../nova/auth/manager.py:428 +#: ../nova/auth/manager.py:431 msgid "Must specify project" msgstr "Deve especificar projeto" @@ -2597,99 +2595,99 @@ msgstr "O papel %s não foi encontrado" msgid "The %s role is global only" msgstr "O papel %s é apenas global" -#: ../nova/auth/manager.py:469 +#: ../nova/auth/manager.py:472 #, python-format msgid "Adding role %(role)s to user %(uid)s in project %(pid)s" msgstr "" -#: ../nova/auth/manager.py:472 +#: ../nova/auth/manager.py:475 #, python-format msgid "Adding sitewide role %(role)s to user %(uid)s" msgstr "" -#: ../nova/auth/manager.py:498 +#: ../nova/auth/manager.py:501 #, python-format msgid "Removing role %(role)s from user %(uid)s on project %(pid)s" msgstr "" -#: ../nova/auth/manager.py:501 +#: ../nova/auth/manager.py:504 #, python-format msgid "Removing sitewide role %(role)s from user %(uid)s" msgstr "" -#: ../nova/auth/manager.py:574 +#: ../nova/auth/manager.py:577 #, python-format msgid "Created project %(name)s with manager %(manager_user)s" msgstr "" -#: ../nova/auth/manager.py:592 +#: ../nova/auth/manager.py:595 #, python-format msgid "modifying project %s" msgstr "modificando projeto %s" -#: ../nova/auth/manager.py:604 +#: ../nova/auth/manager.py:607 #, python-format msgid "Adding user %(uid)s to project %(pid)s" -msgstr "" +msgstr "Adicionando usuário %(uid)s ao projeto %(pid)s" -#: ../nova/auth/manager.py:625 +#: ../nova/auth/manager.py:628 #, python-format msgid "Remove user %(uid)s from project %(pid)s" -msgstr "" +msgstr "Remover usuário %(uid)s do projeto %(pid)s" -#: ../nova/auth/manager.py:655 +#: ../nova/auth/manager.py:658 #, python-format msgid "Deleting project %s" msgstr "Excluindo projeto %s" -#: ../nova/auth/manager.py:713 +#: ../nova/auth/manager.py:716 #, python-format msgid "Created user %(rvname)s (admin: %(rvadmin)r)" msgstr "" -#: ../nova/auth/manager.py:722 +#: ../nova/auth/manager.py:725 #, python-format msgid "Deleting user %s" msgstr "Apagando usuário %s" -#: ../nova/auth/manager.py:732 +#: ../nova/auth/manager.py:735 #, python-format msgid "Access Key change for user %s" msgstr "" -#: ../nova/auth/manager.py:734 +#: ../nova/auth/manager.py:737 #, python-format msgid "Secret Key change for user %s" msgstr "" -#: ../nova/auth/manager.py:736 +#: ../nova/auth/manager.py:739 #, python-format msgid "Admin status set to %(admin)r for user %(uid)s" msgstr "" -#: ../nova/auth/manager.py:781 +#: ../nova/auth/manager.py:784 #, python-format msgid "No vpn data for project %s" msgstr "" -#: ../nova/service.py:141 +#: ../nova/service.py:146 #, python-format msgid "Starting %(topic)s node (version %(vcs_string)s)" msgstr "" -#: ../nova/service.py:232 +#: ../nova/service.py:237 msgid "Service killed that has no database entry" msgstr "Encerrado serviço que não tem entrada na base de dados" -#: ../nova/service.py:269 +#: ../nova/service.py:274 msgid "The service database object disappeared, Recreating it." msgstr "O objeto da base de dados do serviço desapareceu, Recriando." -#: ../nova/service.py:284 +#: ../nova/service.py:289 msgid "Recovered model server connection!" msgstr "Recuperada conexão servidor de modelo." -#: ../nova/service.py:290 +#: ../nova/service.py:295 msgid "model server went away" msgstr "servidor de modelo perdido" @@ -2735,7 +2733,7 @@ msgstr "" msgid "The group at dn %s doesn't exist" msgstr "O grupo no dn %s não existe" -#: ../nova/exception.py:801 +#: ../nova/exception.py:817 #, python-format msgid "User %(uid)s is already a member of the group %(group_dn)s" msgstr "Usuário %(uid)s já é um membro do grupo %(group_dn)s" |