summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorRicardo Carrillo Cruz <emaildericky@gmail.com>2011-02-04 11:48:45 +0100
committerRicardo Carrillo Cruz <emaildericky@gmail.com>2011-02-04 11:48:45 +0100
commitc852a4d48d2d7afe0a7d74b5da4d5b31386bbda3 (patch)
tree4b3fdecca5d391e5b2bdb06a4ea9379a13c0337c /nova
parente35ca46173a5f3bf2d1460c19249fd0bf9f5b538 (diff)
parent7783105dbbfa5c6e6d1ab9cc965929d1c4cc4eef (diff)
downloadnova-c852a4d48d2d7afe0a7d74b5da4d5b31386bbda3.tar.gz
nova-c852a4d48d2d7afe0a7d74b5da4d5b31386bbda3.tar.xz
nova-c852a4d48d2d7afe0a7d74b5da4d5b31386bbda3.zip
merging
Diffstat (limited to 'nova')
-rw-r--r--nova/adminclient.py20
-rw-r--r--nova/api/ec2/__init__.py31
-rw-r--r--nova/api/ec2/admin.py11
-rw-r--r--nova/api/ec2/apirequest.py5
-rw-r--r--nova/api/ec2/cloud.py23
-rw-r--r--nova/api/openstack/__init__.py4
-rw-r--r--nova/api/openstack/common.py2
-rw-r--r--nova/api/openstack/servers.py1
-rw-r--r--nova/compute/api.py2
-rw-r--r--nova/compute/manager.py22
-rw-r--r--nova/db/api.py5
-rw-r--r--nova/db/sqlalchemy/api.py54
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/001_austin.py24
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py24
-rw-r--r--nova/db/sqlalchemy/migration.py11
-rw-r--r--nova/db/sqlalchemy/models.py6
-rw-r--r--nova/exception.py13
-rw-r--r--nova/image/local.py30
-rw-r--r--nova/image/s3.py9
-rw-r--r--nova/log.py7
-rw-r--r--nova/network/linux_net.py11
-rw-r--r--nova/network/manager.py6
-rw-r--r--nova/scheduler/simple.py8
-rw-r--r--nova/service.py5
-rw-r--r--nova/test.py3
-rw-r--r--nova/tests/api/openstack/test_images.py1
-rw-r--r--nova/tests/api/openstack/test_servers.py2
-rw-r--r--nova/tests/db/nova.austin.sqlitebin0 -> 44032 bytes
-rw-r--r--nova/tests/test_api.py34
-rw-r--r--nova/tests/test_compute.py55
-rw-r--r--nova/tests/test_localization.py100
-rw-r--r--nova/tests/test_virt.py20
-rw-r--r--nova/utils.py16
-rw-r--r--nova/version.py2
-rw-r--r--nova/virt/hyperv.py2
-rw-r--r--nova/virt/images.py3
-rw-r--r--nova/virt/libvirt.xml.template6
-rw-r--r--nova/virt/libvirt_conn.py58
-rw-r--r--nova/virt/xenapi/vm_utils.py2
-rw-r--r--nova/virt/xenapi/vmops.py2
40 files changed, 457 insertions, 183 deletions
diff --git a/nova/adminclient.py b/nova/adminclient.py
index 3cdd8347f..c614b274c 100644
--- a/nova/adminclient.py
+++ b/nova/adminclient.py
@@ -21,6 +21,7 @@ Nova User API client library.
import base64
import boto
+import boto.exception
import httplib
from boto.ec2.regioninfo import RegionInfo
@@ -288,10 +289,14 @@ class NovaAdminClient(object):
def get_user(self, name):
"""Grab a single user by name."""
- user = self.apiconn.get_object('DescribeUser', {'Name': name},
- UserInfo)
- if user.username != None:
- return user
+ try:
+ return self.apiconn.get_object('DescribeUser',
+ {'Name': name},
+ UserInfo)
+ except boto.exception.BotoServerError, e:
+ if e.status == 400 and e.error_code == 'NotFound':
+ return None
+ raise
def has_user(self, username):
"""Determine if user exists."""
@@ -376,6 +381,13 @@ class NovaAdminClient(object):
'MemberUsers': member_users}
return self.apiconn.get_object('RegisterProject', params, ProjectInfo)
+ def modify_project(self, projectname, manager_user=None, description=None):
+ """Modifies an existing project."""
+ params = {'Name': projectname,
+ 'ManagerUser': manager_user,
+ 'Description': description}
+ return self.apiconn.get_status('ModifyProject', params)
+
def delete_project(self, projectname):
"""Permanently deletes the specified project."""
return self.apiconn.get_object('DeregisterProject',
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index fc9a37908..ddcdc673c 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -33,6 +33,7 @@ from nova import log as logging
from nova import utils
from nova import wsgi
from nova.api.ec2 import apirequest
+from nova.api.ec2 import cloud
from nova.auth import manager
@@ -170,7 +171,7 @@ class Authenticate(wsgi.Middleware):
req.path)
# Be explicit for what exceptions are 403, the rest bubble as 500
except (exception.NotFound, exception.NotAuthorized) as ex:
- LOG.audit(_("Authentication Failure: %s"), ex.args[0])
+ LOG.audit(_("Authentication Failure: %s"), unicode(ex))
raise webob.exc.HTTPForbidden()
# Authenticated!
@@ -213,7 +214,8 @@ class Requestify(wsgi.Middleware):
LOG.debug(_('arg: %(key)s\t\tval: %(value)s') % locals())
# Success!
- api_request = apirequest.APIRequest(self.controller, action, args)
+ api_request = apirequest.APIRequest(self.controller, action,
+ req.params['Version'], args)
req.environ['ec2.request'] = api_request
req.environ['ec2.action_args'] = args
return self.application
@@ -313,19 +315,32 @@ class Executor(wsgi.Application):
result = None
try:
result = api_request.invoke(context)
+ except exception.InstanceNotFound as ex:
+ LOG.info(_('InstanceNotFound raised: %s'), unicode(ex),
+ context=context)
+ ec2_id = cloud.id_to_ec2_id(ex.instance_id)
+ message = _('Instance %s not found') % ec2_id
+ return self._error(req, context, type(ex).__name__, message)
+ except exception.VolumeNotFound as ex:
+ LOG.info(_('VolumeNotFound raised: %s'), unicode(ex),
+ context=context)
+ ec2_id = cloud.id_to_ec2_id(ex.volume_id, 'vol-%08x')
+ message = _('Volume %s not found') % ec2_id
+ return self._error(req, context, type(ex).__name__, message)
except exception.NotFound as ex:
- LOG.info(_('NotFound raised: %s'), ex.args[0], context=context)
- return self._error(req, context, type(ex).__name__, ex.args[0])
+ LOG.info(_('NotFound raised: %s'), unicode(ex), context=context)
+ return self._error(req, context, type(ex).__name__, unicode(ex))
except exception.ApiError as ex:
- LOG.exception(_('ApiError raised: %s'), ex.args[0],
+ LOG.exception(_('ApiError raised: %s'), unicode(ex),
context=context)
if ex.code:
- return self._error(req, context, ex.code, ex.args[0])
+ return self._error(req, context, ex.code, unicode(ex))
else:
- return self._error(req, context, type(ex).__name__, ex.args[0])
+ return self._error(req, context, type(ex).__name__,
+ unicode(ex))
except Exception as ex:
extra = {'environment': req.environ}
- LOG.exception(_('Unexpected error raised: %s'), ex.args[0],
+ LOG.exception(_('Unexpected error raised: %s'), unicode(ex),
extra=extra, context=context)
return self._error(req,
context,
diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py
index d7e899d12..735951082 100644
--- a/nova/api/ec2/admin.py
+++ b/nova/api/ec2/admin.py
@@ -184,6 +184,17 @@ class AdminController(object):
description=None,
member_users=None))
+ def modify_project(self, context, name, manager_user, description=None,
+ **kwargs):
+ """Modifies a project"""
+ msg = _("Modify project: %(name)s managed by"
+ " %(manager_user)s") % locals()
+ LOG.audit(msg, context=context)
+ manager.AuthManager().modify_project(name,
+ manager_user=manager_user,
+ description=description)
+ return True
+
def deregister_project(self, context, name):
"""Permanently deletes a project."""
LOG.audit(_("Delete project: %s"), name, context=context)
diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py
index d8a2b5f53..7e72d67fb 100644
--- a/nova/api/ec2/apirequest.py
+++ b/nova/api/ec2/apirequest.py
@@ -83,9 +83,10 @@ def _try_convert(value):
class APIRequest(object):
- def __init__(self, controller, action, args):
+ def __init__(self, controller, action, version, args):
self.controller = controller
self.action = action
+ self.version = version
self.args = args
def invoke(self, context):
@@ -132,7 +133,7 @@ class APIRequest(object):
response_el = xml.createElement(self.action + 'Response')
response_el.setAttribute('xmlns',
- 'http://ec2.amazonaws.com/doc/2009-11-30/')
+ 'http://ec2.amazonaws.com/doc/%s/' % self.version)
request_id_el = xml.createElement('requestId')
request_id_el.appendChild(xml.createTextNode(request_id))
response_el.appendChild(request_id_el)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 22b8c19cb..00d044e95 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -532,12 +532,8 @@ class CloudController(object):
volumes = []
for ec2_id in volume_id:
internal_id = ec2_id_to_id(ec2_id)
- try:
- volume = self.volume_api.get(context, internal_id)
- volumes.append(volume)
- except exception.NotFound:
- raise exception.NotFound(_("Volume %s not found")
- % ec2_id)
+ volume = self.volume_api.get(context, internal_id)
+ volumes.append(volume)
else:
volumes = self.volume_api.get_all(context)
volumes = [self._format_volume(context, v) for v in volumes]
@@ -668,12 +664,8 @@ class CloudController(object):
instances = []
for ec2_id in instance_id:
internal_id = ec2_id_to_id(ec2_id)
- try:
- instance = self.compute_api.get(context, internal_id)
- instances.append(instance)
- except exception.NotFound:
- raise exception.NotFound(_("Instance %s not found")
- % ec2_id)
+ instance = self.compute_api.get(context, internal_id)
+ instances.append(instance)
else:
instances = self.compute_api.get_all(context, **kwargs)
for instance in instances:
@@ -722,7 +714,12 @@ class CloudController(object):
r = {}
r['reservationId'] = instance['reservation_id']
r['ownerId'] = instance['project_id']
- r['groupSet'] = self._convert_to_set([], 'groups')
+ security_group_names = []
+ if instance.get('security_groups'):
+ for security_group in instance['security_groups']:
+ security_group_names.append(security_group['name'])
+ r['groupSet'] = self._convert_to_set(security_group_names,
+ 'groupId')
r['instancesSet'] = []
reservations[instance['reservation_id']] = r
reservations[instance['reservation_id']]['instancesSet'].append(i)
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index c70bb39ed..056c7dd27 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -51,8 +51,8 @@ class FaultWrapper(wsgi.Middleware):
try:
return req.get_response(self.application)
except Exception as ex:
- LOG.exception(_("Caught error: %s"), str(ex))
- exc = webob.exc.HTTPInternalServerError(explanation=str(ex))
+ LOG.exception(_("Caught error: %s"), unicode(ex))
+ exc = webob.exc.HTTPInternalServerError(explanation=unicode(ex))
return faults.Fault(exc)
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 037ed47a0..6d2fa16e8 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -54,7 +54,7 @@ def get_image_id_from_image_hash(image_service, context, image_hash):
except NotImplementedError:
items = image_service.index(context)
for image in items:
- image_id = image['imageId']
+ image_id = image['id']
if abs(hash(image_id)) == int(image_hash):
return image_id
raise exception.NotFound(image_hash)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 9d308ea24..17c5519a1 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -138,6 +138,7 @@ class Controller(wsgi.Controller):
_("%(param)s property not found for image %(_image_id)s") %
locals())
+ image_id = str(image_id)
image = self._image_service.show(req.environ['nova.context'], image_id)
return lookup('kernel_id'), lookup('ramdisk_id')
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 1d8b9d79f..ac02dbcfa 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -318,7 +318,7 @@ class API(base.Base):
def get(self, context, instance_id):
"""Get a single instance with the given ID."""
- rv = self.db.instance_get_by_id(context, instance_id)
+ rv = self.db.instance_get(context, instance_id)
return dict(rv.iteritems())
def get_all(self, context, project_id=None, reservation_id=None,
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 0f9bf301f..f4418af26 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -37,7 +37,6 @@ terminating it.
import datetime
import random
import string
-import logging
import socket
import functools
@@ -231,22 +230,25 @@ class ComputeManager(manager.Manager):
instance_ref = self.db.instance_get(context, instance_id)
LOG.audit(_("Terminating instance %s"), instance_id, context=context)
- if not FLAGS.stub_network:
- address = self.db.instance_get_floating_address(context,
- instance_ref['id'])
- if address:
- LOG.debug(_("Disassociating address %s"), address,
+ fixed_ip = instance_ref.get('fixed_ip')
+ if not FLAGS.stub_network and fixed_ip:
+ floating_ips = fixed_ip.get('floating_ips') or []
+ for floating_ip in floating_ips:
+ address = floating_ip['address']
+ LOG.debug("Disassociating address %s", address,
context=context)
# NOTE(vish): Right now we don't really care if the ip is
# disassociated. We may need to worry about
# checking this later.
+ network_topic = self.db.queue_get_for(context,
+ FLAGS.network_topic,
+ floating_ip['host'])
rpc.cast(context,
- self.get_network_topic(context),
+ network_topic,
{"method": "disassociate_floating_ip",
"args": {"floating_address": address}})
- address = self.db.instance_get_fixed_address(context,
- instance_ref['id'])
+ address = fixed_ip['address']
if address:
LOG.debug(_("Deallocating address %s"), address,
context=context)
@@ -256,7 +258,7 @@ class ComputeManager(manager.Manager):
self.network_manager.deallocate_fixed_ip(context.elevated(),
address)
- volumes = instance_ref.get('volumes', []) or []
+ volumes = instance_ref.get('volumes') or []
for volume in volumes:
self.detach_volume(context, instance_id, volume['id'])
if instance_ref['state'] == power_state.SHUTOFF:
diff --git a/nova/db/api.py b/nova/db/api.py
index c6c03fb0e..789cb8ebb 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -379,11 +379,6 @@ def instance_get_project_vpn(context, project_id):
return IMPL.instance_get_project_vpn(context, project_id)
-def instance_get_by_id(context, instance_id):
- """Get an instance by id."""
- return IMPL.instance_get_by_id(context, instance_id)
-
-
def instance_is_vpn(context, instance_id):
"""True if instance is a vpn."""
return IMPL.instance_is_vpn(context, instance_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index fa060228f..85250d56e 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -19,6 +19,7 @@
Implementation of SQLAlchemy backend.
"""
+import datetime
import warnings
from nova import db
@@ -670,8 +671,14 @@ def instance_data_get_for_project(context, project_id):
def instance_destroy(context, instance_id):
session = get_session()
with session.begin():
- instance_ref = instance_get(context, instance_id, session=session)
- instance_ref.delete(session=session)
+ session.execute('update instances set deleted=1,'
+ 'deleted_at=:at where id=:id',
+ {'id': instance_id,
+ 'at': datetime.datetime.utcnow()})
+ session.execute('update security_group_instance_association '
+ 'set deleted=1,deleted_at=:at where instance_id=:id',
+ {'id': instance_id,
+ 'at': datetime.datetime.utcnow()})
@require_context
@@ -685,6 +692,7 @@ def instance_get(context, instance_id, session=None):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload_all('security_groups.rules')).\
options(joinedload('volumes')).\
+ options(joinedload_all('fixed_ip.network')).\
filter_by(id=instance_id).\
filter_by(deleted=can_read_deleted(context)).\
first()
@@ -698,7 +706,9 @@ def instance_get(context, instance_id, session=None):
filter_by(deleted=False).\
first()
if not result:
- raise exception.NotFound(_('No instance for id %s') % instance_id)
+ raise exception.InstanceNotFound(_('Instance %s not found')
+ % instance_id,
+ instance_id)
return result
@@ -782,33 +792,6 @@ def instance_get_project_vpn(context, project_id):
@require_context
-def instance_get_by_id(context, instance_id):
- session = get_session()
-
- if is_admin_context(context):
- result = session.query(models.Instance).\
- options(joinedload_all('fixed_ip.floating_ips')).\
- options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.network')).\
- filter_by(id=instance_id).\
- filter_by(deleted=can_read_deleted(context)).\
- first()
- elif is_user_context(context):
- result = session.query(models.Instance).\
- options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ip.floating_ips')).\
- options(joinedload_all('fixed_ip.network')).\
- filter_by(project_id=context.project_id).\
- filter_by(id=instance_id).\
- filter_by(deleted=False).\
- first()
- if not result:
- raise exception.NotFound(_('Instance %s not found') % (instance_id))
-
- return result
-
-
-@require_context
def instance_get_fixed_address(context, instance_id):
session = get_session()
with session.begin():
@@ -1419,7 +1402,8 @@ def volume_get(context, volume_id, session=None):
filter_by(deleted=False).\
first()
if not result:
- raise exception.NotFound(_('No volume for id %s') % volume_id)
+ raise exception.VolumeNotFound(_('Volume %s not found') % volume_id,
+ volume_id)
return result
@@ -1464,7 +1448,8 @@ def volume_get_instance(context, volume_id):
options(joinedload('instance')).\
first()
if not result:
- raise exception.NotFound(_('Volume %s not found') % ec2_id)
+ raise exception.VolumeNotFound(_('Volume %s not found') % volume_id,
+ volume_id)
return result.instance
@@ -1605,6 +1590,11 @@ def security_group_destroy(context, security_group_id):
# TODO(vish): do we have to use sql here?
session.execute('update security_groups set deleted=1 where id=:id',
{'id': security_group_id})
+ session.execute('update security_group_instance_association '
+ 'set deleted=1,deleted_at=:at '
+ 'where security_group_id=:id',
+ {'id': security_group_id,
+ 'at': datetime.datetime.utcnow()})
session.execute('update security_group_rules set deleted=1 '
'where group_id=:id',
{'id': security_group_id})
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py
index a312a7190..366944591 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py
@@ -134,6 +134,9 @@ instances = Table('instances', meta,
Column('ramdisk_id',
String(length=255, convert_unicode=False, assert_unicode=None,
unicode_error=None, _warn_on_bytestring=False)),
+ Column('server_name',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
Column('launch_index', Integer()),
Column('key_name',
String(length=255, convert_unicode=False, assert_unicode=None,
@@ -178,23 +181,6 @@ instances = Table('instances', meta,
)
-iscsi_targets = Table('iscsi_targets', meta,
- Column('created_at', DateTime(timezone=False)),
- Column('updated_at', DateTime(timezone=False)),
- Column('deleted_at', DateTime(timezone=False)),
- Column('deleted', Boolean(create_constraint=True, name=None)),
- Column('id', Integer(), primary_key=True, nullable=False),
- Column('target_num', Integer()),
- Column('host',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('volume_id',
- Integer(),
- ForeignKey('volumes.id'),
- nullable=True),
- )
-
-
key_pairs = Table('key_pairs', meta,
Column('created_at', DateTime(timezone=False)),
Column('updated_at', DateTime(timezone=False)),
@@ -523,7 +509,7 @@ def upgrade(migrate_engine):
meta.bind = migrate_engine
for table in (auth_tokens, export_devices, fixed_ips, floating_ips,
- instances, iscsi_targets, key_pairs, networks,
+ instances, key_pairs, networks,
projects, quotas, security_groups, security_group_inst_assoc,
security_group_rules, services, users,
user_project_association, user_project_role_association,
@@ -539,7 +525,7 @@ def upgrade(migrate_engine):
def downgrade(migrate_engine):
# Operations to reverse the above upgrade go here.
for table in (auth_tokens, export_devices, fixed_ips, floating_ips,
- instances, iscsi_targets, key_pairs, networks,
+ instances, key_pairs, networks,
projects, quotas, security_groups, security_group_inst_assoc,
security_group_rules, services, users,
user_project_association, user_project_role_association,
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
index bd3a3e6f8..699b837f8 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
@@ -41,6 +41,10 @@ networks = Table('networks', meta,
Column('id', Integer(), primary_key=True, nullable=False),
)
+volumes = Table('volumes', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
#
# New Tables
@@ -131,6 +135,23 @@ instance_actions = Table('instance_actions', meta,
)
+iscsi_targets = Table('iscsi_targets', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('target_num', Integer()),
+ Column('host',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('volume_id',
+ Integer(),
+ ForeignKey('volumes.id'),
+ nullable=True),
+ )
+
+
#
# Tables to alter
#
@@ -188,7 +209,8 @@ def upgrade(migrate_engine):
# Upgrade operations go here. Don't create your own engine;
# bind migrate_engine to your metadata
meta.bind = migrate_engine
- for table in (certificates, consoles, console_pools, instance_actions):
+ for table in (certificates, consoles, console_pools, instance_actions,
+ iscsi_targets):
try:
table.create()
except Exception:
diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py
index 33d14827b..2a13c5466 100644
--- a/nova/db/sqlalchemy/migration.py
+++ b/nova/db/sqlalchemy/migration.py
@@ -46,12 +46,15 @@ def db_version():
meta.reflect(bind=engine)
try:
for table in ('auth_tokens', 'export_devices', 'fixed_ips',
- 'floating_ips', 'instances', 'iscsi_targets',
+ 'floating_ips', 'instances',
'key_pairs', 'networks', 'projects', 'quotas',
- 'security_group_rules',
- 'security_group_instance_association', 'services',
+ 'security_group_instance_association',
+ 'security_group_rules', 'security_groups',
+ 'services',
'users', 'user_project_association',
- 'user_project_role_association', 'volumes'):
+ 'user_project_role_association',
+ 'user_role_association',
+ 'volumes'):
assert table in meta.tables
return db_version_control(1)
except AssertionError:
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index c54ebe3ba..7efb36c0e 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -311,10 +311,14 @@ class SecurityGroup(BASE, NovaBase):
secondary="security_group_instance_association",
primaryjoin='and_('
'SecurityGroup.id == '
- 'SecurityGroupInstanceAssociation.security_group_id,'
+ 'SecurityGroupInstanceAssociation.security_group_id,'
+ 'SecurityGroupInstanceAssociation.deleted == False,'
'SecurityGroup.deleted == False)',
secondaryjoin='and_('
'SecurityGroupInstanceAssociation.instance_id == Instance.id,'
+ # (anthony) the condition below shouldn't be necessary now that the
+ # association is being marked as deleted. However, removing this
+ # may cause existing deployments to choke, so I'm leaving it
'Instance.deleted == False)',
backref='security_groups')
diff --git a/nova/exception.py b/nova/exception.py
index f604fd63a..7d65bd6a5 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -46,7 +46,6 @@ class Error(Exception):
class ApiError(Error):
-
def __init__(self, message='Unknown', code='Unknown'):
self.message = message
self.code = code
@@ -57,6 +56,18 @@ class NotFound(Error):
pass
+class InstanceNotFound(NotFound):
+ def __init__(self, message, instance_id):
+ self.instance_id = instance_id
+ super(InstanceNotFound, self).__init__(message)
+
+
+class VolumeNotFound(NotFound):
+ def __init__(self, message, volume_id):
+ self.volume_id = volume_id
+ super(VolumeNotFound, self).__init__(message)
+
+
class Duplicate(Error):
pass
diff --git a/nova/image/local.py b/nova/image/local.py
index b44593221..f78b9aa89 100644
--- a/nova/image/local.py
+++ b/nova/image/local.py
@@ -18,6 +18,7 @@
import cPickle as pickle
import os.path
import random
+import tempfile
from nova import exception
from nova.image import service
@@ -26,15 +27,12 @@ from nova.image import service
class LocalImageService(service.BaseImageService):
"""Image service storing images to local disk.
+ It assumes that image_ids are integers.
- It assumes that image_ids are integers."""
+ """
def __init__(self):
- self._path = "/tmp/nova/images"
- try:
- os.makedirs(self._path)
- except OSError: # Exists
- pass
+ self._path = tempfile.mkdtemp()
def _path_to(self, image_id):
return os.path.join(self._path, str(image_id))
@@ -56,9 +54,7 @@ class LocalImageService(service.BaseImageService):
raise exception.NotFound
def create(self, context, data):
- """
- Store the image data and return the new image id.
- """
+ """Store the image data and return the new image id."""
id = random.randint(0, 2 ** 31 - 1)
data['id'] = id
self.update(context, id, data)
@@ -72,8 +68,9 @@ class LocalImageService(service.BaseImageService):
raise exception.NotFound
def delete(self, context, image_id):
- """
- Delete the given image. Raises OSError if the image does not exist.
+ """Delete the given image.
+ Raises OSError if the image does not exist.
+
"""
try:
os.unlink(self._path_to(image_id))
@@ -81,8 +78,13 @@ class LocalImageService(service.BaseImageService):
raise exception.NotFound
def delete_all(self):
- """
- Clears out all images in local directory
- """
+ """Clears out all images in local directory."""
for id in self._ids():
os.unlink(self._path_to(id))
+
+ def delete_imagedir(self):
+ """Deletes the local directory.
+ Raises OSError if directory is not empty.
+
+ """
+ os.rmdir(self._path)
diff --git a/nova/image/s3.py b/nova/image/s3.py
index 7b04aa072..08a40f191 100644
--- a/nova/image/s3.py
+++ b/nova/image/s3.py
@@ -65,12 +65,19 @@ class S3ImageService(service.BaseImageService):
'image_id': image_id}))
return image_id
+ def _fix_image_id(self, images):
+ """S3 has imageId but OpenStack wants id"""
+ for image in images:
+ if 'imageId' in image:
+ image['id'] = image['imageId']
+ return images
+
def index(self, context):
"""Return a list of all images that a user can see."""
response = self._conn(context).make_request(
method='GET',
bucket='_images')
- return json.loads(response.read())
+ return self._fix_image_id(json.loads(response.read()))
def show(self, context, image_id):
"""return a image object if the context has permissions"""
diff --git a/nova/log.py b/nova/log.py
index e1c9f46f4..b541488bd 100644
--- a/nova/log.py
+++ b/nova/log.py
@@ -31,6 +31,7 @@ import cStringIO
import json
import logging
import logging.handlers
+import sys
import traceback
from nova import flags
@@ -191,6 +192,12 @@ class NovaLogger(logging.Logger):
kwargs.pop('exc_info')
self.error(message, **kwargs)
+
+def handle_exception(type, value, tb):
+ logging.root.critical(str(value), exc_info=(type, value, tb))
+
+
+sys.excepthook = handle_exception
logging.setLoggerClass(NovaLogger)
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index d29e17603..cdd1f666a 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -198,9 +198,9 @@ def ensure_bridge(bridge, interface, net_attrs=None):
net_attrs['broadcast'],
net_attrs['netmask']))
if(FLAGS.use_ipv6):
- _execute("sudo ifconfig %s add %s up" % \
- (bridge,
- net_attrs['cidr_v6']))
+ _execute("sudo ip -f inet6 addr change %s dev %s" %
+ (net_attrs['cidr_v6'], bridge))
+ _execute("sudo ifconfig %s up" % bridge)
else:
_execute("sudo ifconfig %s up" % bridge)
if FLAGS.use_nova_chains:
@@ -298,10 +298,9 @@ interface %s
% pid, check_exit_code=False)
if conffile in out:
try:
- _execute('sudo kill -HUP %d' % pid)
- return
+ _execute('sudo kill %d' % pid)
except Exception as exc: # pylint: disable-msg=W0703
- LOG.debug(_("Hupping radvd threw %s"), exc)
+ LOG.debug(_("killing radvd threw %s"), exc)
else:
LOG.debug(_("Pid %d is stale, relaunching radvd"), pid)
command = _ra_cmd(network_ref)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index fe99f2612..fbcbea131 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -428,6 +428,10 @@ class FlatDHCPManager(FlatManager):
self.driver.ensure_bridge(network_ref['bridge'],
FLAGS.flat_interface,
network_ref)
+ if not FLAGS.fake_network:
+ self.driver.update_dhcp(context, network_id)
+ if(FLAGS.use_ipv6):
+ self.driver.update_ra(context, network_id)
class VlanManager(NetworkManager):
@@ -497,7 +501,7 @@ class VlanManager(NetworkManager):
network_ref['bridge'])
def create_networks(self, context, cidr, num_networks, network_size,
- vlan_start, vpn_start, cidr_v6):
+ cidr_v6, vlan_start, vpn_start):
"""Create networks based on parameters."""
fixed_net = IPy.IP(cidr)
fixed_net_v6 = IPy.IP(cidr_v6)
diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py
index baf4966d4..0191ceb3d 100644
--- a/nova/scheduler/simple.py
+++ b/nova/scheduler/simple.py
@@ -43,7 +43,9 @@ class SimpleScheduler(chance.ChanceScheduler):
def schedule_run_instance(self, context, instance_id, *_args, **_kwargs):
"""Picks a host that is up and has the fewest running instances."""
instance_ref = db.instance_get(context, instance_id)
- if instance_ref['availability_zone'] and context.is_admin:
+ if (instance_ref['availability_zone']
+ and ':' in instance_ref['availability_zone']
+ and context.is_admin):
zone, _x, host = instance_ref['availability_zone'].partition(':')
service = db.service_get_by_args(context.elevated(), host,
'nova-compute')
@@ -75,7 +77,9 @@ class SimpleScheduler(chance.ChanceScheduler):
def schedule_create_volume(self, context, volume_id, *_args, **_kwargs):
"""Picks a host that is up and has the fewest volumes."""
volume_ref = db.volume_get(context, volume_id)
- if (':' in volume_ref['availability_zone']) and context.is_admin:
+ if (volume_ref['availability_zone']
+ and ':' in volume_ref['availability_zone']
+ and context.is_admin):
zone, _x, host = volume_ref['availability_zone'].partition(':')
service = db.service_get_by_args(context.elevated(), host,
'nova-volume')
diff --git a/nova/service.py b/nova/service.py
index 2c30997f2..59648adf2 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -157,8 +157,9 @@ class Service(object):
report_interval = FLAGS.report_interval
if not periodic_interval:
periodic_interval = FLAGS.periodic_interval
- logging.audit(_("Starting %s node (version %s)"), topic,
- version.version_string_with_vcs())
+ vcs_string = version.version_string_with_vcs()
+ logging.audit(_("Starting %(topic)s node (version %(vcs_string)s)")
+ % locals())
service_obj = cls(host, binary, topic, manager,
report_interval, periodic_interval)
diff --git a/nova/test.py b/nova/test.py
index 881baccd5..a12cf9d32 100644
--- a/nova/test.py
+++ b/nova/test.py
@@ -69,9 +69,10 @@ class TestCase(unittest.TestCase):
network_manager.VlanManager().create_networks(ctxt,
FLAGS.fixed_range,
5, 16,
+ FLAGS.fixed_range_v6,
FLAGS.vlan_start,
FLAGS.vpn_start,
- FLAGS.fixed_range_v6)
+ )
# emulate some of the mox stuff, we can't use the metaclass
# because it screws with our generators
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index 5d9ddefbe..8ab4d7569 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -143,6 +143,7 @@ class LocalImageServiceTest(unittest.TestCase,
def tearDown(self):
self.service.delete_all()
+ self.service.delete_imagedir()
self.stubs.UnsetAll()
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 29883e7c8..724f14f19 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -76,7 +76,7 @@ class ServersTest(unittest.TestCase):
fakes.stub_out_key_pair_funcs(self.stubs)
fakes.stub_out_image_service(self.stubs)
self.stubs.Set(nova.db.api, 'instance_get_all', return_servers)
- self.stubs.Set(nova.db.api, 'instance_get_by_id', return_server)
+ self.stubs.Set(nova.db.api, 'instance_get', return_server)
self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
return_servers)
self.stubs.Set(nova.db.api, 'instance_add_security_group',
diff --git a/nova/tests/db/nova.austin.sqlite b/nova/tests/db/nova.austin.sqlite
new file mode 100644
index 000000000..ad1326bce
--- /dev/null
+++ b/nova/tests/db/nova.austin.sqlite
Binary files differ
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index 66a16b0cb..2569e262b 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -36,6 +36,7 @@ from nova.auth import manager
class FakeHttplibSocket(object):
"""a fake socket implementation for httplib.HTTPResponse, trivial"""
def __init__(self, response_string):
+ self.response_string = response_string
self._buffer = StringIO.StringIO(response_string)
def makefile(self, _mode, _other):
@@ -66,13 +67,16 @@ class FakeHttplibConnection(object):
# For some reason, the response doesn't have "HTTP/1.0 " prepended; I
# guess that's a function the web server usually provides.
resp = "HTTP/1.0 %s" % resp
- sock = FakeHttplibSocket(resp)
- self.http_response = httplib.HTTPResponse(sock)
+ self.sock = FakeHttplibSocket(resp)
+ self.http_response = httplib.HTTPResponse(self.sock)
self.http_response.begin()
def getresponse(self):
return self.http_response
+ def getresponsebody(self):
+ return self.sock.response_string
+
def close(self):
"""Required for compatibility with boto/tornado"""
pass
@@ -104,7 +108,7 @@ class ApiEc2TestCase(test.TestCase):
self.app = ec2.Authenticate(ec2.Requestify(ec2.Executor(),
'nova.api.ec2.cloud.CloudController'))
- def expect_http(self, host=None, is_secure=False):
+ def expect_http(self, host=None, is_secure=False, api_version=None):
"""Returns a new EC2 connection"""
self.ec2 = boto.connect_ec2(
aws_access_key_id='fake',
@@ -113,13 +117,31 @@ class ApiEc2TestCase(test.TestCase):
region=regioninfo.RegionInfo(None, 'test', self.host),
port=8773,
path='/services/Cloud')
+ if api_version:
+ self.ec2.APIVersion = api_version
self.mox.StubOutWithMock(self.ec2, 'new_http_connection')
- http = FakeHttplibConnection(
+ self.http = FakeHttplibConnection(
self.app, '%s:8773' % (self.host), False)
# pylint: disable-msg=E1103
- self.ec2.new_http_connection(host, is_secure).AndReturn(http)
- return http
+ self.ec2.new_http_connection(host, is_secure).AndReturn(self.http)
+ return self.http
+
+ def test_xmlns_version_matches_request_version(self):
+ self.expect_http(api_version='2010-10-30')
+ self.mox.ReplayAll()
+
+ user = self.manager.create_user('fake', 'fake', 'fake')
+ project = self.manager.create_project('fake', 'fake', 'fake')
+
+ # Any request should be fine
+ self.ec2.get_all_instances()
+ self.assertTrue(self.ec2.APIVersion in self.http.getresponsebody(),
+ 'The version in the xmlns of the response does '
+ 'not match the API version given in the request.')
+
+ self.manager.delete_project(project)
+ self.manager.delete_user(user)
def test_describe_instances(self):
"""Test that, after creating a user and a project, the describe
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 09f6ee94a..2aa0690e7 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -49,7 +49,7 @@ class ComputeTestCase(test.TestCase):
self.manager = manager.AuthManager()
self.user = self.manager.create_user('fake', 'fake', 'fake')
self.project = self.manager.create_project('fake', 'fake', 'fake')
- self.context = context.get_admin_context()
+ self.context = context.RequestContext('fake', 'fake', False)
def tearDown(self):
self.manager.delete_user(self.user)
@@ -69,6 +69,13 @@ class ComputeTestCase(test.TestCase):
inst['ami_launch_index'] = 0
return db.instance_create(self.context, inst)['id']
+ def _create_group(self):
+ values = {'name': 'testgroup',
+ 'description': 'testgroup',
+ 'user_id': self.user.id,
+ 'project_id': self.project.id}
+ return db.security_group_create(self.context, values)
+
def test_create_instance_defaults_display_name(self):
"""Verify that an instance cannot be created without a display_name."""
cases = [dict(), dict(display_name=None)]
@@ -82,21 +89,53 @@ class ComputeTestCase(test.TestCase):
def test_create_instance_associates_security_groups(self):
"""Make sure create associates security groups"""
- values = {'name': 'default',
- 'description': 'default',
- 'user_id': self.user.id,
- 'project_id': self.project.id}
- group = db.security_group_create(self.context, values)
+ group = self._create_group()
ref = self.compute_api.create(
self.context,
instance_type=FLAGS.default_instance_type,
image_id=None,
- security_group=['default'])
+ security_group=['testgroup'])
try:
self.assertEqual(len(db.security_group_get_by_instance(
- self.context, ref[0]['id'])), 1)
+ self.context, ref[0]['id'])), 1)
+ group = db.security_group_get(self.context, group['id'])
+ self.assert_(len(group.instances) == 1)
+ finally:
+ db.security_group_destroy(self.context, group['id'])
+ db.instance_destroy(self.context, ref[0]['id'])
+
+ def test_destroy_instance_disassociates_security_groups(self):
+ """Make sure destroying disassociates security groups"""
+ group = self._create_group()
+
+ ref = self.compute_api.create(
+ self.context,
+ instance_type=FLAGS.default_instance_type,
+ image_id=None,
+ security_group=['testgroup'])
+ try:
+ db.instance_destroy(self.context, ref[0]['id'])
+ group = db.security_group_get(self.context, group['id'])
+ self.assert_(len(group.instances) == 0)
finally:
db.security_group_destroy(self.context, group['id'])
+
+ def test_destroy_security_group_disassociates_instances(self):
+ """Make sure destroying security groups disassociates instances"""
+ group = self._create_group()
+
+ ref = self.compute_api.create(
+ self.context,
+ instance_type=FLAGS.default_instance_type,
+ image_id=None,
+ security_group=['testgroup'])
+
+ try:
+ db.security_group_destroy(self.context, group['id'])
+ group = db.security_group_get(context.get_admin_context(
+ read_deleted=True), group['id'])
+ self.assert_(len(group.instances) == 0)
+ finally:
db.instance_destroy(self.context, ref[0]['id'])
def test_run_terminate(self):
diff --git a/nova/tests/test_localization.py b/nova/tests/test_localization.py
new file mode 100644
index 000000000..6992773f5
--- /dev/null
+++ b/nova/tests/test_localization.py
@@ -0,0 +1,100 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2011 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 glob
+import logging
+import os
+import re
+import sys
+import unittest
+
+import nova
+
+
+class LocalizationTestCase(unittest.TestCase):
+ def test_multiple_positional_format_placeholders(self):
+ pat = re.compile("\W_\(")
+ single_pat = re.compile("\W%\W")
+ root_path = os.path.dirname(nova.__file__)
+ problems = {}
+ for root, dirs, files in os.walk(root_path):
+ for fname in files:
+ if not fname.endswith(".py"):
+ continue
+ pth = os.path.join(root, fname)
+ txt = fulltext = file(pth).read()
+ txt_lines = fulltext.splitlines()
+ if not pat.search(txt):
+ continue
+ problems[pth] = []
+ pos = txt.find("_(")
+ while pos > -1:
+ # Make sure that this isn't part of a dunder;
+ # e.g., __init__(...
+ # or something like 'self.assert_(...'
+ test_txt = txt[pos - 1: pos + 10]
+ if not (pat.search(test_txt)):
+ txt = txt[pos + 2:]
+ pos = txt.find("_(")
+ continue
+ pos += 2
+ txt = txt[pos:]
+ innerChars = []
+ # Count pairs of open/close parens until _() closing
+ # paren is found.
+ parenCount = 1
+ pos = 0
+ while parenCount > 0:
+ char = txt[pos]
+ if char == "(":
+ parenCount += 1
+ elif char == ")":
+ parenCount -= 1
+ innerChars.append(char)
+ pos += 1
+ inner_all = "".join(innerChars)
+ # Filter out '%%' and '%('
+ inner = inner_all.replace("%%", "").replace("%(", "")
+ # Filter out the single '%' operators
+ inner = single_pat.sub("", inner)
+ # Within the remaining content, count %
+ fmtCount = inner.count("%")
+ if fmtCount > 1:
+ inner_first = inner_all.splitlines()[0]
+ lns = ["%s" % (p + 1)
+ for p, t in enumerate(txt_lines)
+ if inner_first in t]
+ lnums = ", ".join(lns)
+ # Using ugly string concatenation to avoid having
+ # this test fail itself.
+ inner_all = "_" + "(" + "%s" % inner_all
+ problems[pth].append("Line: %s Text: %s" %
+ (lnums, inner_all))
+ # Look for more
+ pos = txt.find("_(")
+ if not problems[pth]:
+ del problems[pth]
+ if problems:
+ out = ["Problem(s) found in localized string formatting",
+ "(see http://www.gnu.org/software/hello/manual/"
+ "gettext/Python.html for more information)",
+ "",
+ " ------------ Files to fix ------------"]
+ for pth in problems:
+ out.append(" %s:" % pth)
+ for val in set(problems[pth]):
+ out.append(" %s" % val)
+ raise AssertionError("\n".join(out))
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index 0b9b847a0..6e5a0114b 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -221,7 +221,12 @@ class IptablesFirewallTestCase(test.TestCase):
self.project = self.manager.create_project('fake', 'fake', 'fake')
self.context = context.RequestContext('fake', 'fake')
self.network = utils.import_object(FLAGS.network_manager)
- self.fw = libvirt_conn.IptablesFirewallDriver()
+
+ class FakeLibvirtConnection(object):
+ pass
+ self.fake_libvirt_connection = FakeLibvirtConnection()
+ self.fw = libvirt_conn.IptablesFirewallDriver(
+ get_connection=lambda: self.fake_libvirt_connection)
def tearDown(self):
self.manager.delete_project(self.project)
@@ -474,6 +479,19 @@ class NWFilterTestCase(test.TestCase):
'project_id': 'fake'})
inst_id = instance_ref['id']
+ ip = '10.11.12.13'
+
+ network_ref = db.project_get_network(self.context,
+ 'fake')
+
+ fixed_ip = {'address': ip,
+ 'network_id': network_ref['id']}
+
+ admin_ctxt = context.get_admin_context()
+ db.fixed_ip_create(admin_ctxt, fixed_ip)
+ db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
+ 'instance_id': instance_ref['id']})
+
def _ensure_all_called():
instance_filter = 'nova-instance-%s' % instance_ref['name']
secgroup_filter = 'nova-secgroup-%s' % self.security_group['id']
diff --git a/nova/utils.py b/nova/utils.py
index 2f3bd2894..5f5225289 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -206,21 +206,17 @@ def last_octet(address):
def get_my_linklocal(interface):
try:
if_str = execute("ip -f inet6 -o addr show %s" % interface)
- condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope\s+link"
+ condition = "\s+inet6\s+([0-9a-f:]+)/\d+\s+scope\s+link"
links = [re.search(condition, x) for x in if_str[0].split('\n')]
address = [w.group(1) for w in links if w is not None]
if address[0] is not None:
return address[0]
else:
- return 'fe00::'
- except IndexError as ex:
- LOG.warn(_("Couldn't get Link Local IP of %(interface)s :%(ex)s")
- % locals())
- except ProcessExecutionError as ex:
- LOG.warn(_("Couldn't get Link Local IP of %(interface)s :%(ex)s")
- % locals())
- except:
- return 'fe00::'
+ raise exception.Error(_("Link Local address is not found.:%s")
+ % if_str)
+ except Exception as ex:
+ raise exception.Error(_("Couldn't get Link Local IP of %(interface)s"
+ " :%(ex)s") % locals())
def to_global_ipv6(prefix, mac):
diff --git a/nova/version.py b/nova/version.py
index 7b27acb6a..c3ecc2245 100644
--- a/nova/version.py
+++ b/nova/version.py
@@ -21,7 +21,7 @@ except ImportError:
'revision_id': 'LOCALREVISION',
'revno': 0}
-NOVA_VERSION = ['2011', '1']
+NOVA_VERSION = ['2011', '2']
YEAR, COUNT = NOVA_VERSION
FINAL = False # This becomes true at Release Candidate time
diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py
index 5afa3221d..29d18dac5 100644
--- a/nova/virt/hyperv.py
+++ b/nova/virt/hyperv.py
@@ -191,7 +191,7 @@ class HyperVConnection(object):
vcpus = long(instance['vcpus'])
procsetting.VirtualQuantity = vcpus
procsetting.Reservation = vcpus
- procsetting.Limit = vcpus
+ procsetting.Limit = 100000 # static assignment to 100%
(job, ret_val) = vs_man_svc.ModifyVirtualSystemResources(
vm.path_(), [procsetting.GetText_(1)])
diff --git a/nova/virt/images.py b/nova/virt/images.py
index 9c987e14d..7a6fef330 100644
--- a/nova/virt/images.py
+++ b/nova/virt/images.py
@@ -111,5 +111,8 @@ def _image_path(path):
def image_url(image):
+ if FLAGS.image_service == "nova.image.glance.GlanceImageService":
+ return "http://%s:%s/images/%s" % (FLAGS.glance_host,
+ FLAGS.glance_port, image)
return "http://%s:%s/_images/%s/image" % (FLAGS.s3_host, FLAGS.s3_port,
image)
diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template
index 8139c3620..88bfbc668 100644
--- a/nova/virt/libvirt.xml.template
+++ b/nova/virt/libvirt.xml.template
@@ -75,11 +75,13 @@
<!-- <model type='virtio'/> CANT RUN virtio network right now -->
<filterref filter="nova-instance-${name}">
<parameter name="IP" value="${ip_address}" />
- <parameter name="DHCPSERVER" value="${dhcp_server}" />
- <parameter name="RASERVER" value="${ra_server}" />
+ <parameter name="DHCPSERVER" value="${dhcp_server}" />
#if $getVar('extra_params', False)
${extra_params}
#end if
+#if $getVar('ra_server', False)
+ <parameter name="RASERVER" value="${ra_server}" />
+#end if
</filterref>
</interface>
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index bd5c9c4ee..4e0fd106f 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -149,13 +149,8 @@ class LibvirtConnection(object):
self._wrapped_conn = None
self.read_only = read_only
- self.nwfilter = NWFilterFirewall(self._get_connection)
-
- if not FLAGS.firewall_driver:
- self.firewall_driver = self.nwfilter
- self.nwfilter.handle_security_groups = True
- else:
- self.firewall_driver = utils.import_object(FLAGS.firewall_driver)
+ fw_class = utils.import_class(FLAGS.firewall_driver)
+ self.firewall_driver = fw_class(get_connection=self._get_connection)
def init_host(self, host):
# Adopt existing VM's running here
@@ -409,7 +404,7 @@ class LibvirtConnection(object):
instance['id'],
power_state.NOSTATE,
'launching')
- self.nwfilter.setup_basic_filtering(instance)
+ self.firewall_driver.setup_basic_filtering(instance)
self.firewall_driver.prepare_instance_filter(instance)
self._create_image(instance, xml)
self._conn.createXML(xml, 0)
@@ -678,8 +673,7 @@ class LibvirtConnection(object):
# Assume that the gateway also acts as the dhcp server.
dhcp_server = network['gateway']
ra_server = network['ra_server']
- if not ra_server:
- ra_server = 'fd00::'
+
if FLAGS.allow_project_net_traffic:
if FLAGS.use_ipv6:
net, mask = _get_net_and_mask(network['cidr'])
@@ -718,11 +712,13 @@ class LibvirtConnection(object):
'mac_address': instance['mac_address'],
'ip_address': ip_address,
'dhcp_server': dhcp_server,
- 'ra_server': ra_server,
'extra_params': extra_params,
'rescue': rescue,
'local': instance_type['local_gb'],
'driver_type': driver_type}
+
+ if ra_server:
+ xml_info['ra_server'] = ra_server + "/128"
if not rescue:
if instance['kernel_id']:
xml_info['kernel'] = xml_info['basepath'] + "/kernel"
@@ -905,6 +901,20 @@ class FirewallDriver(object):
the security group."""
raise NotImplementedError()
+ def setup_basic_filtering(self, instance):
+ """Create rules to block spoofing and allow dhcp.
+
+ This gets called when spawning an instance, before
+ :method:`prepare_instance_filter`.
+
+ """
+ raise NotImplementedError()
+
+ def _ra_server_for_instance(self, instance):
+ network = db.network_get_by_instance(context.get_admin_context(),
+ instance['id'])
+ return network['ra_server']
+
class NWFilterFirewall(FirewallDriver):
"""
@@ -952,11 +962,15 @@ class NWFilterFirewall(FirewallDriver):
"""
- def __init__(self, get_connection):
+ def __init__(self, get_connection, **kwargs):
self._libvirt_get_connection = get_connection
self.static_filters_configured = False
self.handle_security_groups = False
+ def apply_instance_filter(self, instance):
+ """No-op. Everything is done in prepare_instance_filter"""
+ pass
+
def _get_connection(self):
return self._libvirt_get_connection()
_conn = property(_get_connection)
@@ -1115,7 +1129,9 @@ class NWFilterFirewall(FirewallDriver):
'nova-base-ipv6',
'nova-allow-dhcp-server']
if FLAGS.use_ipv6:
- instance_secgroup_filter_children += ['nova-allow-ra-server']
+ ra_server = self._ra_server_for_instance(instance)
+ if ra_server:
+ instance_secgroup_filter_children += ['nova-allow-ra-server']
ctxt = context.get_admin_context()
@@ -1142,10 +1158,6 @@ class NWFilterFirewall(FirewallDriver):
return
- def apply_instance_filter(self, instance):
- """No-op. Everything is done in prepare_instance_filter"""
- pass
-
def refresh_security_group_rules(self, security_group_id):
return self._define_filter(
self.security_group_to_nwfilter_xml(security_group_id))
@@ -1193,9 +1205,14 @@ class NWFilterFirewall(FirewallDriver):
class IptablesFirewallDriver(FirewallDriver):
- def __init__(self, execute=None):
+ def __init__(self, execute=None, **kwargs):
self.execute = execute or utils.execute
self.instances = {}
+ self.nwfilter = NWFilterFirewall(kwargs['get_connection'])
+
+ def setup_basic_filtering(self, instance):
+ """Use NWFilter from libvirt for this."""
+ return self.nwfilter.setup_basic_filtering(instance)
def apply_instance_filter(self, instance):
"""No-op. Everything is done in prepare_instance_filter"""
@@ -1301,8 +1318,9 @@ class IptablesFirewallDriver(FirewallDriver):
elif(ip_version == 6):
# Allow RA responses
ra_server = self._ra_server_for_instance(instance)
- our_rules += ['-A %s -s %s -p icmpv6 '
- '-j ACCEPT' % (chain_name, ra_server)]
+ if ra_server:
+ our_rules += ['-A %s -s %s -p icmpv6 -j ACCEPT' %
+ (chain_name, ra_server + "/128")]
#Allow project network traffic
if (FLAGS.allow_project_net_traffic):
cidrv6 = self._project_cidrv6_for_instance(instance)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 4afd28dd8..4bbd522c1 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -640,7 +640,7 @@ def with_vdi_attached_here(session, vdi, read_only, f):
session.get_xenapi().VBD.plug(vbd)
LOG.debug(_('Plugging VBD %s done.'), vbd)
orig_dev = session.get_xenapi().VBD.get_device(vbd)
- LOG.debug(_('VBD %s plugged as %s'), vbd, orig_dev)
+ LOG.debug(_('VBD %(vbd)s plugged as %(orig_dev)s') % locals())
dev = remap_vbd_dev(orig_dev)
if dev != orig_dev:
LOG.debug(_('VBD %(vbd)s plugged into wrong dev, '
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 628a171fa..e84ce20c4 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -149,7 +149,7 @@ class VMOps(object):
if isinstance(instance_or_vm, (int, long)):
ctx = context.get_admin_context()
try:
- instance_obj = db.instance_get_by_id(ctx, instance_or_vm)
+ instance_obj = db.instance_get(ctx, instance_or_vm)
instance_name = instance_obj.name
except exception.NotFound:
# The unit tests screw this up, as they use an integer for