summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Erdfelt <johannes.erdfelt@rackspace.com>2011-09-12 20:48:43 +0000
committerJohannes Erdfelt <johannes.erdfelt@rackspace.com>2011-09-12 20:48:43 +0000
commit79ef79abf24f7b8c8d3c9b652285a69fee7e9d14 (patch)
treee98ac047eb52616eda118be617c4229dc4189f47
parent2351c06f50d4556e564a7b0abb1653805e661330 (diff)
parent68551bb375588470b41606f181c445192c18cdaa (diff)
Merge with trunk
-rwxr-xr-xbin/nova-manage3
-rwxr-xr-xbin/nova-vncproxy15
-rw-r--r--nova/api/auth.py1
-rw-r--r--nova/api/openstack/create_instance_helper.py3
-rw-r--r--nova/api/openstack/servers.py5
-rw-r--r--nova/compute/api.py8
-rw-r--r--nova/compute/manager.py7
-rw-r--r--nova/context.py9
-rw-r--r--nova/db/api.py6
-rw-r--r--nova/db/sqlalchemy/api.py9
-rw-r--r--nova/image/__init__.py56
-rw-r--r--nova/image/glance.py91
-rw-r--r--nova/network/manager.py3
-rw-r--r--nova/scheduler/abstract_scheduler.py2
-rw-r--r--nova/scheduler/base_scheduler.py7
-rw-r--r--nova/tests/api/openstack/fakes.py2
-rw-r--r--nova/tests/glance/stubs.py6
-rw-r--r--nova/tests/integrated/integrated_helpers.py2
-rw-r--r--nova/tests/test_compute.py15
-rw-r--r--nova/tests/test_libvirt.py4
-rw-r--r--nova/tests/test_network.py3
-rw-r--r--nova/tests/test_virt_drivers.py3
-rw-r--r--nova/tests/test_vmwareapi.py3
-rw-r--r--nova/tests/test_xenapi.py3
-rw-r--r--nova/virt/disk.py20
-rw-r--r--nova/virt/driver.py3
-rw-r--r--nova/virt/fake.py2
-rw-r--r--nova/virt/hyperv.py2
-rw-r--r--nova/virt/images.py3
-rw-r--r--nova/virt/libvirt/connection.py6
-rw-r--r--nova/virt/vmwareapi/fake.py6
-rw-r--r--nova/virt/vmwareapi/vmops.py4
-rw-r--r--nova/virt/vmwareapi/vmware_images.py69
-rw-r--r--nova/virt/vmwareapi_conn.py2
-rw-r--r--nova/virt/xenapi/vm_utils.py17
-rw-r--r--nova/virt/xenapi/vmops.py13
-rw-r--r--nova/virt/xenapi_conn.py4
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/glance6
38 files changed, 208 insertions, 215 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index bc191b2f0..089b2eeae 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -724,8 +724,7 @@ class NetworkCommands(object):
bridge_interface = bridge_interface or FLAGS.flat_interface or \
FLAGS.vlan_interface
if not bridge_interface:
- interface_required = ['nova.network.manager.FlatDHCPManager',
- 'nova.network.manager.VlanManager']
+ interface_required = ['nova.network.manager.VlanManager']
if FLAGS.network_manager in interface_required:
raise exception.NetworkNotCreated(req='--bridge_interface')
diff --git a/bin/nova-vncproxy b/bin/nova-vncproxy
index dc08e2433..8e75451cb 100755
--- a/bin/nova-vncproxy
+++ b/bin/nova-vncproxy
@@ -107,10 +107,13 @@ if __name__ == "__main__":
else:
with_auth = auth.VNCNovaAuthMiddleware(with_logging)
- server = wsgi.Server("VNC Proxy",
- with_auth,
- host=FLAGS.vncproxy_host,
- port=FLAGS.vncproxy_port)
- server.start_tcp(handle_flash_socket_policy, 843, host=FLAGS.vncproxy_host)
- service.serve(server)
+ wsgi_server = wsgi.Server("VNC Proxy",
+ with_auth,
+ host=FLAGS.vncproxy_host,
+ port=FLAGS.vncproxy_port)
+ wsgi_server.start_tcp(handle_flash_socket_policy,
+ 843,
+ host=FLAGS.vncproxy_host)
+ server = service.Service.create(binary='nova-vncproxy')
+ service.serve(wsgi_server, server)
service.wait()
diff --git a/nova/api/auth.py b/nova/api/auth.py
index cd0d38b3f..f73cae01e 100644
--- a/nova/api/auth.py
+++ b/nova/api/auth.py
@@ -70,6 +70,7 @@ class KeystoneContext(wsgi.Middleware):
project_id,
roles=roles,
auth_token=auth_token,
+ strategy='keystone',
remote_address=remote_address)
req.environ['nova.context'] = ctx
diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py
index 67e669c17..e27ddf78b 100644
--- a/nova/api/openstack/create_instance_helper.py
+++ b/nova/api/openstack/create_instance_helper.py
@@ -92,7 +92,8 @@ class CreateInstanceHelper(object):
if str(image_href).startswith(req.application_url):
image_href = image_href.split('/').pop()
try:
- image_service, image_id = nova.image.get_image_service(image_href)
+ image_service, image_id = nova.image.get_image_service(context,
+ image_href)
kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image(
req, image_service, image_id)
images = set([str(x['id']) for x in image_service.index(context)])
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index d084ac360..f5447edc5 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -334,9 +334,8 @@ class Controller(object):
LOG.exception(msg)
raise exc.HTTPBadRequest(explanation=msg)
try:
- # TODO(gundlach): pass reboot_type, support soft reboot in
- # virt driver
- self.compute_api.reboot(req.environ['nova.context'], id)
+ self.compute_api.reboot(req.environ['nova.context'], id,
+ reboot_type)
except Exception, e:
LOG.exception(_("Error in reboot %s"), e)
raise exc.HTTPUnprocessableEntity()
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 81747d09c..1f4ce3165 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -215,7 +215,8 @@ class API(base.Base):
self._check_injected_file_quota(context, injected_files)
self._check_requested_networks(context, requested_networks)
- (image_service, image_id) = nova.image.get_image_service(image_href)
+ (image_service, image_id) = nova.image.get_image_service(context,
+ image_href)
image = image_service.show(context, image_id)
config_drive_id = None
@@ -1101,13 +1102,14 @@ class API(base.Base):
return recv_meta
@scheduler_api.reroute_compute("reboot")
- def reboot(self, context, instance_id):
+ def reboot(self, context, instance_id, reboot_type):
"""Reboot the given instance."""
self.update(context,
instance_id,
vm_state=vm_states.ACTIVE,
task_state=task_states.REBOOTING)
- self._cast_compute_message('reboot_instance', context, instance_id)
+ self._cast_compute_message('reboot_instance', context, instance_id,
+ reboot_type)
@scheduler_api.reroute_compute("rebuild")
def rebuild(self, context, instance_id, image_href, admin_password,
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 5c1cbd5f7..91236dd3c 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -325,7 +325,8 @@ class ComputeManager(manager.SchedulerDependentManager):
# used by the image service. This should be refactored to be
# consistent.
image_href = instance['image_ref']
- image_service, image_id = nova.image.get_image_service(image_href)
+ image_service, image_id = nova.image.get_image_service(context,
+ image_href)
image_meta = image_service.show(context, image_id)
try:
@@ -608,7 +609,7 @@ class ComputeManager(manager.SchedulerDependentManager):
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@checks_instance_lock
- def reboot_instance(self, context, instance_id):
+ def reboot_instance(self, context, instance_id, reboot_type="SOFT"):
"""Reboot an instance on this host."""
LOG.audit(_("Rebooting instance %s"), instance_id, context=context)
context = context.elevated()
@@ -630,7 +631,7 @@ class ComputeManager(manager.SchedulerDependentManager):
context=context)
network_info = self._get_instance_nw_info(context, instance_ref)
- self.driver.reboot(instance_ref, network_info)
+ self.driver.reboot(instance_ref, network_info, reboot_type)
current_power_state = self._get_power_state(context, instance_ref)
self._instance_update(context,
diff --git a/nova/context.py b/nova/context.py
index 5c22641a0..de5b791c4 100644
--- a/nova/context.py
+++ b/nova/context.py
@@ -32,7 +32,7 @@ class RequestContext(object):
def __init__(self, user_id, project_id, is_admin=None, read_deleted=False,
roles=None, remote_address=None, timestamp=None,
- request_id=None, auth_token=None):
+ request_id=None, auth_token=None, strategy='noauth'):
self.user_id = user_id
self.project_id = project_id
self.roles = roles or []
@@ -50,6 +50,7 @@ class RequestContext(object):
request_id = unicode(uuid.uuid4())
self.request_id = request_id
self.auth_token = auth_token
+ self.strategy = strategy
def to_dict(self):
return {'user_id': self.user_id,
@@ -60,7 +61,8 @@ class RequestContext(object):
'remote_address': self.remote_address,
'timestamp': utils.strtime(self.timestamp),
'request_id': self.request_id,
- 'auth_token': self.auth_token}
+ 'auth_token': self.auth_token,
+ 'strategy': self.strategy}
@classmethod
def from_dict(cls, values):
@@ -77,7 +79,8 @@ class RequestContext(object):
remote_address=self.remote_address,
timestamp=self.timestamp,
request_id=self.request_id,
- auth_token=self.auth_token)
+ auth_token=self.auth_token,
+ strategy=self.strategy)
def get_admin_context(read_deleted=False):
diff --git a/nova/db/api.py b/nova/db/api.py
index c03a86671..a9d2dc065 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -324,13 +324,15 @@ def migration_get_by_instance_and_status(context, instance_uuid, status):
####################
-def fixed_ip_associate(context, address, instance_id, network_id=None):
+def fixed_ip_associate(context, address, instance_id, network_id=None,
+ reserved=False):
"""Associate fixed ip to instance.
Raises if fixed ip is not available.
"""
- return IMPL.fixed_ip_associate(context, address, instance_id, network_id)
+ return IMPL.fixed_ip_associate(context, address, instance_id, network_id,
+ reserved)
def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None):
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 523258841..e5a661c7f 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -669,14 +669,19 @@ def floating_ip_update(context, address, values):
@require_admin_context
-def fixed_ip_associate(context, address, instance_id, network_id=None):
+def fixed_ip_associate(context, address, instance_id, network_id=None,
+ reserved=False):
+ """Keyword arguments:
+ reserved -- should be a boolean value(True or False), exact value will be
+ used to filter on the fixed ip address
+ """
session = get_session()
with session.begin():
network_or_none = or_(models.FixedIp.network_id == network_id,
models.FixedIp.network_id == None)
fixed_ip_ref = session.query(models.FixedIp).\
filter(network_or_none).\
- filter_by(reserved=False).\
+ filter_by(reserved=reserved).\
filter_by(deleted=False).\
filter_by(address=address).\
with_lockmode('update').\
diff --git a/nova/image/__init__.py b/nova/image/__init__.py
index 5447c8a3a..307b73f01 100644
--- a/nova/image/__init__.py
+++ b/nova/image/__init__.py
@@ -16,70 +16,20 @@
# under the License.
-from urlparse import urlparse
-
import nova
-from nova import exception
from nova import utils
from nova import flags
-from nova.image import glance as glance_image_service
+from nova.image import glance
FLAGS = flags.FLAGS
-GlanceClient = utils.import_class('glance.client.Client')
-
-
-def _parse_image_ref(image_href):
- """Parse an image href into composite parts.
-
- :param image_href: href of an image
- :returns: a tuple of the form (image_id, host, port)
- :raises ValueError
-
- """
- o = urlparse(image_href)
- port = o.port or 80
- host = o.netloc.split(':', 1)[0]
- image_id = int(o.path.split('/')[-1])
- return (image_id, host, port)
-
-
def get_default_image_service():
ImageService = utils.import_class(FLAGS.image_service)
return ImageService()
-# FIXME(sirp): perhaps this should be moved to nova/images/glance so that we
-# keep Glance specific code together for the most part
-def get_glance_client(image_href):
- """Get the correct glance client and id for the given image_href.
-
- The image_href param can be an href of the form
- http://myglanceserver:9292/images/42, or just an int such as 42. If the
- image_href is an int, then flags are used to create the default
- glance client.
-
- :param image_href: image ref/id for an image
- :returns: a tuple of the form (glance_client, image_id)
-
- """
- image_href = image_href or 0
- if str(image_href).isdigit():
- glance_host, glance_port = \
- glance_image_service.pick_glance_api_server()
- glance_client = GlanceClient(glance_host, glance_port)
- return (glance_client, int(image_href))
-
- try:
- (image_id, host, port) = _parse_image_ref(image_href)
- except ValueError:
- raise exception.InvalidImageRef(image_href=image_href)
- glance_client = GlanceClient(host, port)
- return (glance_client, image_id)
-
-
-def get_image_service(image_href):
+def get_image_service(context, image_href):
"""Get the proper image_service and id for the given image_href.
The image_href param can be an href of the form
@@ -94,6 +44,6 @@ def get_image_service(image_href):
if str(image_href).isdigit():
return (get_default_image_service(), int(image_href))
- (glance_client, image_id) = get_glance_client(image_href)
+ (glance_client, image_id) = glance.get_glance_client(context, image_href)
image_service = nova.image.glance.GlanceImageService(glance_client)
return (image_service, image_id)
diff --git a/nova/image/glance.py b/nova/image/glance.py
index 80abc7384..13c8ff843 100644
--- a/nova/image/glance.py
+++ b/nova/image/glance.py
@@ -23,6 +23,7 @@ import copy
import datetime
import json
import random
+from urlparse import urlparse
from glance.common import exception as glance_exception
@@ -42,6 +43,35 @@ FLAGS = flags.FLAGS
GlanceClient = utils.import_class('glance.client.Client')
+def _parse_image_ref(image_href):
+ """Parse an image href into composite parts.
+
+ :param image_href: href of an image
+ :returns: a tuple of the form (image_id, host, port)
+ :raises ValueError
+
+ """
+ o = urlparse(image_href)
+ port = o.port or 80
+ host = o.netloc.split(':', 1)[0]
+ image_id = int(o.path.split('/')[-1])
+ return (image_id, host, port)
+
+
+def _create_glance_client(context, host, port):
+ if context.strategy == 'keystone':
+ # NOTE(dprince): Glance client just needs auth_tok right? Should we
+ # add username and tenant to the creds below?
+ creds = {'strategy': 'keystone',
+ 'username': context.user_id,
+ 'tenant': context.project_id}
+ glance_client = GlanceClient(host, port, auth_tok=context.auth_token,
+ creds=creds)
+ else:
+ glance_client = GlanceClient(host, port)
+ return glance_client
+
+
def pick_glance_api_server():
"""Return which Glance API server to use for the request
@@ -57,6 +87,33 @@ def pick_glance_api_server():
return host, port
+def get_glance_client(context, image_href):
+ """Get the correct glance client and id for the given image_href.
+
+ The image_href param can be an href of the form
+ http://myglanceserver:9292/images/42, or just an int such as 42. If the
+ image_href is an int, then flags are used to create the default
+ glance client.
+
+ :param image_href: image ref/id for an image
+ :returns: a tuple of the form (glance_client, image_id)
+
+ """
+ image_href = image_href or 0
+ if str(image_href).isdigit():
+ glance_host, glance_port = pick_glance_api_server()
+ glance_client = _create_glance_client(context, glance_host,
+ glance_port)
+ return (glance_client, int(image_href))
+
+ try:
+ (image_id, host, port) = _parse_image_ref(image_href)
+ except ValueError:
+ raise exception.InvalidImageRef(image_href=image_href)
+ glance_client = _create_glance_client(context, glance_host, glance_port)
+ return (glance_client, image_id)
+
+
class GlanceImageService(service.BaseImageService):
"""Provides storage and retrieval of disk image objects within Glance."""
@@ -71,23 +128,14 @@ class GlanceImageService(service.BaseImageService):
def __init__(self, client=None):
self._client = client
- def _get_client(self):
+ def _get_client(self, context):
# NOTE(sirp): we want to load balance each request across glance
# servers. Since GlanceImageService is a long-lived object, `client`
# is made to choose a new server each time via this property.
if self._client is not None:
return self._client
glance_host, glance_port = pick_glance_api_server()
- return GlanceClient(glance_host, glance_port)
-
- def _set_client(self, client):
- self._client = client
-
- client = property(_get_client, _set_client)
-
- def _set_client_context(self, context):
- """Sets the client's auth token."""
- self.client.set_auth_token(context.auth_token)
+ return _create_glance_client(context, glance_host, glance_port)
def index(self, context, **kwargs):
"""Calls out to Glance for a list of images available."""
@@ -128,14 +176,14 @@ class GlanceImageService(service.BaseImageService):
def _get_images(self, context, **kwargs):
"""Get image entitites from images service"""
- self._set_client_context(context)
# ensure filters is a dict
kwargs['filters'] = kwargs.get('filters') or {}
# NOTE(vish): don't filter out private images
kwargs['filters'].setdefault('is_public', 'none')
- return self._fetch_images(self.client.get_images_detailed, **kwargs)
+ client = self._get_client(context)
+ return self._fetch_images(client.get_images_detailed, **kwargs)
def _fetch_images(self, fetch_func, **kwargs):
"""Paginate through results from glance server"""
@@ -168,9 +216,8 @@ class GlanceImageService(service.BaseImageService):
def show(self, context, image_id):
"""Returns a dict with image data for the given opaque image id."""
- self._set_client_context(context)
try:
- image_meta = self.client.get_image_meta(image_id)
+ image_meta = self._get_client(context).get_image_meta(image_id)
except glance_exception.NotFound:
raise exception.ImageNotFound(image_id=image_id)
@@ -192,9 +239,9 @@ class GlanceImageService(service.BaseImageService):
def get(self, context, image_id, data):
"""Calls out to Glance for metadata and data and writes data."""
- self._set_client_context(context)
try:
- image_meta, image_chunks = self.client.get_image(image_id)
+ client = self._get_client(context)
+ image_meta, image_chunks = client.get_image(image_id)
except glance_exception.NotFound:
raise exception.ImageNotFound(image_id=image_id)
@@ -210,7 +257,6 @@ class GlanceImageService(service.BaseImageService):
:raises: AlreadyExists if the image already exist.
"""
- self._set_client_context(context)
# Translate Base -> Service
LOG.debug(_('Creating image in Glance. Metadata passed in %s'),
image_meta)
@@ -218,7 +264,7 @@ class GlanceImageService(service.BaseImageService):
LOG.debug(_('Metadata after formatting for Glance %s'),
sent_service_image_meta)
- recv_service_image_meta = self.client.add_image(
+ recv_service_image_meta = self._get_client(context).add_image(
sent_service_image_meta, data)
# Translate Service -> Base
@@ -233,12 +279,12 @@ class GlanceImageService(service.BaseImageService):
:raises: ImageNotFound if the image does not exist.
"""
- self._set_client_context(context)
# NOTE(vish): show is to check if image is available
self.show(context, image_id)
image_meta = _convert_to_string(image_meta)
try:
- image_meta = self.client.update_image(image_id, image_meta, data)
+ client = self._get_client(context)
+ image_meta = client.update_image(image_id, image_meta, data)
except glance_exception.NotFound:
raise exception.ImageNotFound(image_id=image_id)
@@ -251,11 +297,10 @@ class GlanceImageService(service.BaseImageService):
:raises: ImageNotFound if the image does not exist.
"""
- self._set_client_context(context)
# NOTE(vish): show is to check if image is available
self.show(context, image_id)
try:
- result = self.client.delete_image(image_id)
+ result = self._get_client(context).delete_image(image_id)
except glance_exception.NotFound:
raise exception.ImageNotFound(image_id=image_id)
return result
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 05d928fab..da360720b 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -1005,7 +1005,8 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
address = network['vpn_private_address']
self.db.fixed_ip_associate(context,
address,
- instance_id)
+ instance_id,
+ reserved=True)
else:
address = kwargs.get('address', None)
if address:
diff --git a/nova/scheduler/abstract_scheduler.py b/nova/scheduler/abstract_scheduler.py
index e5ea0f4e4..6e8c7d715 100644
--- a/nova/scheduler/abstract_scheduler.py
+++ b/nova/scheduler/abstract_scheduler.py
@@ -20,8 +20,8 @@ customize the behavior: filter_hosts() and weigh_hosts(). The default
behavior is to simply select all hosts and weight them the same.
"""
-import operator
import json
+import operator
import M2Crypto
diff --git a/nova/scheduler/base_scheduler.py b/nova/scheduler/base_scheduler.py
index e9c078b81..e8629ca92 100644
--- a/nova/scheduler/base_scheduler.py
+++ b/nova/scheduler/base_scheduler.py
@@ -27,6 +27,8 @@ from nova.scheduler import abstract_scheduler
from nova.scheduler import host_filter
FLAGS = flags.FLAGS
+flags.DEFINE_boolean('spread_first', False,
+ 'Use a spread-first zone scheduler strategy')
LOG = logging.getLogger('nova.scheduler.base_scheduler')
@@ -68,4 +70,9 @@ class BaseScheduler(abstract_scheduler.AbstractScheduler):
if num_instances > 0:
instances.extend(hosts[:num_instances])
+ # Adjust the weights for a spread-first strategy
+ if FLAGS.spread_first:
+ for i, host in enumerate(hosts):
+ host['weight'] = i + 1
+
return instances
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 44681d395..098b1e284 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -124,7 +124,7 @@ def stub_out_key_pair_funcs(stubs, have_key_pair=True):
def stub_out_image_service(stubs):
- def fake_get_image_service(image_href):
+ def fake_get_image_service(context, image_href):
return (nova.image.fake.FakeImageService(), image_href)
stubs.Set(nova.image, 'get_image_service', fake_get_image_service)
stubs.Set(nova.image, 'get_default_image_service',
diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py
index f2a19f22d..6b74e671c 100644
--- a/nova/tests/glance/stubs.py
+++ b/nova/tests/glance/stubs.py
@@ -16,14 +16,14 @@
import StringIO
-import nova.image
+from nova.image import glance
def stubout_glance_client(stubs):
- def fake_get_glance_client(image_href):
+ def fake_get_glance_client(context, image_href):
image_id = int(str(image_href).split('/')[-1])
return (FakeGlance('foo'), image_id)
- stubs.Set(nova.image, 'get_glance_client', fake_get_glance_client)
+ stubs.Set(glance, 'get_glance_client', fake_get_glance_client)
class FakeGlance(object):
diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py
index 964ddfa37..b53e4cec6 100644
--- a/nova/tests/integrated/integrated_helpers.py
+++ b/nova/tests/integrated/integrated_helpers.py
@@ -64,7 +64,7 @@ class _IntegratedTestBase(test.TestCase):
self.flags(**f)
self.flags(verbose=True)
- def fake_get_image_service(image_href):
+ def fake_get_image_service(context, image_href):
image_id = int(str(image_href).split('/')[-1])
return (nova.image.fake.FakeImageService(), image_id)
self.stubs.Set(nova.image, 'get_image_service', fake_get_image_service)
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 65fdffbd6..4d463572b 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -300,11 +300,20 @@ class ComputeTestCase(test.TestCase):
self.compute.resume_instance(self.context, instance_id)
self.compute.terminate_instance(self.context, instance_id)
- def test_reboot(self):
- """Ensure instance can be rebooted"""
+ def test_soft_reboot(self):
+ """Ensure instance can be soft rebooted"""
instance_id = self._create_instance()
+ reboot_type = "SOFT"
self.compute.run_instance(self.context, instance_id)
- self.compute.reboot_instance(self.context, instance_id)
+ self.compute.reboot_instance(self.context, instance_id, reboot_type)
+ self.compute.terminate_instance(self.context, instance_id)
+
+ def test_hard_reboot(self):
+ """Ensure instance can be hard rebooted"""
+ instance_id = self._create_instance()
+ reboot_type = "HARD"
+ self.compute.run_instance(self.context, instance_id)
+ self.compute.reboot_instance(self.context, instance_id, reboot_type)
self.compute.terminate_instance(self.context, instance_id)
def test_set_admin_password(self):
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 93967ceec..233ee14de 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -700,7 +700,7 @@ class LibvirtConnTestCase(test.TestCase):
# qemu-img should be mockd since test environment might not have
# large disk space.
self.mox.StubOutWithMock(utils, "execute")
- utils.execute('sudo', 'qemu-img', 'create', '-f', 'raw',
+ utils.execute('qemu-img', 'create', '-f', 'raw',
'%s/%s/disk' % (tmpdir, instance_ref.name), '10G')
self.mox.ReplayAll()
@@ -752,7 +752,7 @@ class LibvirtConnTestCase(test.TestCase):
os.path.getsize("/test/disk").AndReturn(10 * 1024 * 1024 * 1024)
# another is qcow image, so qemu-img should be mocked.
self.mox.StubOutWithMock(utils, "execute")
- utils.execute('sudo', 'qemu-img', 'info', '/test/disk.local').\
+ utils.execute('qemu-img', 'info', '/test/disk.local').\
AndReturn((ret, ''))
self.mox.ReplayAll()
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index c947bbc58..926ea065a 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -286,7 +286,8 @@ class VlanNetworkTestCase(test.TestCase):
db.fixed_ip_associate(mox.IgnoreArg(),
mox.IgnoreArg(),
- mox.IgnoreArg()).AndReturn('192.168.0.1')
+ mox.IgnoreArg(),
+ reserved=True).AndReturn('192.168.0.1')
db.fixed_ip_update(mox.IgnoreArg(),
mox.IgnoreArg(),
mox.IgnoreArg())
diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py
index 480247c91..440d3401b 100644
--- a/nova/tests/test_virt_drivers.py
+++ b/nova/tests/test_virt_drivers.py
@@ -103,8 +103,9 @@ class _VirtDriverTestCase(test.TestCase):
def test_reboot(self):
instance_ref = test_utils.get_test_instance()
network_info = test_utils.get_test_network_info()
+ reboot_type = "SOFT"
self.connection.spawn(self.ctxt, instance_ref, network_info)
- self.connection.reboot(instance_ref, network_info)
+ self.connection.reboot(instance_ref, network_info, reboot_type)
@catch_notimplementederror
def test_get_host_ip_addr(self):
diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py
index 06daf46e8..e6da1690f 100644
--- a/nova/tests/test_vmwareapi.py
+++ b/nova/tests/test_vmwareapi.py
@@ -170,7 +170,8 @@ class VMWareAPIVMTestCase(test.TestCase):
self._create_vm()
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.RUNNING)
- self.conn.reboot(self.instance, self.network_info)
+ reboot_type = "SOFT"
+ self.conn.reboot(self.instance, self.network_info, reboot_type)
info = self.conn.get_info(1)
self._check_vm_info(info, power_state.RUNNING)
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 91b4161b0..4a83d139e 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -932,8 +932,9 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase):
self.fake_instance.architecture = 'x86-64'
def assert_disk_type(self, disk_type):
+ ctx = context.RequestContext('fake', 'fake')
dt = vm_utils.VMHelper.determine_disk_image_type(
- self.fake_instance)
+ self.fake_instance, ctx)
self.assertEqual(disk_type, dt)
def test_instance_disk(self):
diff --git a/nova/virt/disk.py b/nova/virt/disk.py
index 52b2881e8..cd3422829 100644
--- a/nova/virt/disk.py
+++ b/nova/virt/disk.py
@@ -58,7 +58,7 @@ def extend(image, size):
file_size = os.path.getsize(image)
if file_size >= size:
return
- utils.execute('truncate', '-s', size, image)
+ utils.execute('qemu-img', 'resize', image, size)
# NOTE(vish): attempts to resize filesystem
utils.execute('e2fsck', '-fp', image, check_exit_code=False)
utils.execute('resize2fs', image, check_exit_code=False)
@@ -148,15 +148,17 @@ def destroy_container(target, instance, nbd=False):
LXC does not support qcow2 images yet.
"""
+ out, err = utils.execute('mount', run_as_root=True)
+ for loop in out.splitlines():
+ if instance['name'] in loop:
+ device = loop.split()[0]
+
try:
container_dir = '%s/rootfs' % target
utils.execute('umount', container_dir, run_as_root=True)
- finally:
- out, err = utils.execute('losetup', '-a', run_as_root=True)
- for loop in out.splitlines():
- if instance['name'] in loop:
- device = loop.split(loop, ':')
- _unlink_device(device, nbd)
+ _unlink_device(device, nbd)
+ except Exception, exn:
+ LOG.exception(_('Failed to remove container: %s'), exn)
def _link_device(image, nbd):
@@ -228,8 +230,8 @@ def _inject_metadata_into_fs(metadata, fs, execute=None):
metadata_path = os.path.join(fs, "meta.js")
metadata = dict([(m.key, m.value) for m in metadata])
- utils.execute('sudo', 'tee', metadata_path,
- process_input=json.dumps(metadata))
+ utils.execute('tee', metadata_path,
+ process_input=json.dumps(metadata), run_as_root=True)
def _inject_key_into_fs(key, fs, execute=None):
diff --git a/nova/virt/driver.py b/nova/virt/driver.py
index 69d50717a..188ed0a52 100644
--- a/nova/virt/driver.py
+++ b/nova/virt/driver.py
@@ -165,12 +165,13 @@ class ComputeDriver(object):
# TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()
- def reboot(self, instance, network_info):
+ def reboot(self, instance, network_info, reboot_type):
"""Reboot the specified instance.
:param instance: Instance object as returned by DB layer.
:param network_info:
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
+ :param reboot_type: Either a HARD or SOFT reboot
"""
# TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index d5e2bf31b..3596d8353 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -103,7 +103,7 @@ class FakeConnection(driver.ComputeDriver):
if not instance['name'] in self.instances:
raise exception.InstanceNotRunning()
- def reboot(self, instance, network_info):
+ def reboot(self, instance, network_info, reboot_type):
pass
def get_host_ip_addr(self):
diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py
index 03a78db1f..76925b405 100644
--- a/nova/virt/hyperv.py
+++ b/nova/virt/hyperv.py
@@ -367,7 +367,7 @@ class HyperVConnection(driver.ComputeDriver):
wmi_obj.Properties_.Item(prop).Value
return newinst
- def reboot(self, instance, network_info):
+ def reboot(self, instance, network_info, reboot_type):
"""Reboot the specified instance."""
vm = self._lookup(instance.name)
if vm is None:
diff --git a/nova/virt/images.py b/nova/virt/images.py
index 54c691a40..810b359d9 100644
--- a/nova/virt/images.py
+++ b/nova/virt/images.py
@@ -37,7 +37,8 @@ def fetch(context, image_href, path, _user_id, _project_id):
# when it is added to glance. Right now there is no
# auth checking in glance, so we assume that access was
# checked before we got here.
- (image_service, image_id) = nova.image.get_image_service(image_href)
+ (image_service, image_id) = nova.image.get_image_service(context,
+ image_href)
with open(path, "wb") as image_file:
metadata = image_service.get(context, image_id, image_file)
return metadata
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index 19cef5ad7..f591ce02c 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -196,7 +196,7 @@ class LibvirtConnection(driver.ComputeDriver):
def _test_connection(self):
try:
- self._wrapped_conn.getInfo()
+ self._wrapped_conn.getCapabilities()
return True
except libvirt.libvirtError as e:
if e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR and \
@@ -398,10 +398,10 @@ class LibvirtConnection(driver.ComputeDriver):
virt_dom = self._lookup_by_name(instance['name'])
(image_service, image_id) = nova.image.get_image_service(
- instance['image_ref'])
+ context, instance['image_ref'])
base = image_service.show(context, image_id)
(snapshot_image_service, snapshot_image_id) = \
- nova.image.get_image_service(image_href)
+ nova.image.get_image_service(context, image_href)
snapshot = snapshot_image_service.show(context, snapshot_image_id)
metadata = {'is_public': False,
diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py
index 4c62d18bb..0dea13aba 100644
--- a/nova/virt/vmwareapi/fake.py
+++ b/nova/virt/vmwareapi/fake.py
@@ -412,7 +412,7 @@ def fake_get_network(*args, **kwargs):
return [{'type': 'fake'}]
-def fake_fetch_image(image, instance, **kwargs):
+def fake_fetch_image(context, image, instance, **kwargs):
"""Fakes fetch image call. Just adds a reference to the db for the file."""
ds_name = kwargs.get("datastore_name")
file_path = kwargs.get("file_path")
@@ -420,12 +420,12 @@ def fake_fetch_image(image, instance, **kwargs):
_add_file(ds_file_path)
-def fake_upload_image(image, instance, **kwargs):
+def fake_upload_image(context, image, instance, **kwargs):
"""Fakes the upload of an image."""
pass
-def fake_get_vmdk_size_and_properties(image_id, instance):
+def fake_get_vmdk_size_and_properties(context, image_id, instance):
"""Fakes the file size and properties fetch for the image file."""
props = {"vmware_ostype": "otherGuest",
"vmware_adaptertype": "lsiLogic"}
diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py
index 07a6ba6ab..6bdc2f23a 100644
--- a/nova/virt/vmwareapi/vmops.py
+++ b/nova/virt/vmwareapi/vmops.py
@@ -157,7 +157,7 @@ class VMWareVMOps(object):
repository.
"""
image_size, image_properties = \
- vmware_images.get_vmdk_size_and_properties(
+ vmware_images.get_vmdk_size_and_properties(context,
instance.image_ref, instance)
vmdk_file_size_in_kb = int(image_size) / 1024
os_type = image_properties.get("vmware_ostype", "otherGuest")
@@ -282,6 +282,7 @@ class VMWareVMOps(object):
# Upload the -flat.vmdk file whose meta-data file we just created
# above
vmware_images.fetch_image(
+ context,
instance.image_ref,
instance,
host=self._session._host_ip,
@@ -448,6 +449,7 @@ class VMWareVMOps(object):
# Upload the contents of -flat.vmdk file which has the disk data.
LOG.debug(_("Uploading image %s") % snapshot_name)
vmware_images.upload_image(
+ context,
snapshot_name,
instance,
os_type=os_type,
diff --git a/nova/virt/vmwareapi/vmware_images.py b/nova/virt/vmwareapi/vmware_images.py
index f5f75dae2..53f2d372e 100644
--- a/nova/virt/vmwareapi/vmware_images.py
+++ b/nova/virt/vmwareapi/vmware_images.py
@@ -20,15 +20,13 @@ Utility functions for Image transfer.
from nova import exception
from nova import flags
-import nova.image
+from nova.image import glance
from nova import log as logging
from nova.virt.vmwareapi import io_util
from nova.virt.vmwareapi import read_write_util
LOG = logging.getLogger("nova.virt.vmwareapi.vmware_images")
-FLAGS = flags.FLAGS
-
QUEUE_BUFFER_SIZE = 10
@@ -87,36 +85,10 @@ def start_transfer(read_file_handle, data_size, write_file_handle=None,
write_file_handle.close()
-def fetch_image(image, instance, **kwargs):
- """Fetch an image for attaching to the newly created VM."""
- # Depending upon the image service, make appropriate image service call
- if FLAGS.image_service == "nova.image.glance.GlanceImageService":
- func = _get_glance_image
- elif FLAGS.image_service == "nova.image.s3.S3ImageService":
- func = _get_s3_image
- else:
- raise NotImplementedError(_("The Image Service %s is not implemented")
- % FLAGS.image_service)
- return func(image, instance, **kwargs)
-
-
-def upload_image(image, instance, **kwargs):
- """Upload the newly snapshotted VM disk file."""
- # Depending upon the image service, make appropriate image service call
- if FLAGS.image_service == "nova.image.glance.GlanceImageService":
- func = _put_glance_image
- elif FLAGS.image_service == "nova.image.s3.S3ImageService":
- func = _put_s3_image
- else:
- raise NotImplementedError(_("The Image Service %s is not implemented")
- % FLAGS.image_service)
- return func(image, instance, **kwargs)
-
-
-def _get_glance_image(image, instance, **kwargs):
+def fetch_image(context, image, instance, **kwargs):
"""Download image from the glance image server."""
LOG.debug(_("Downloading image %s from glance image server") % image)
- (glance_client, image_id) = nova.image.get_glance_client(image)
+ (glance_client, image_id) = glance.get_glance_client(context, image)
metadata, read_iter = glance_client.get_image(image_id)
read_file_handle = read_write_util.GlanceFileRead(read_iter)
file_size = int(metadata['size'])
@@ -132,17 +104,7 @@ def _get_glance_image(image, instance, **kwargs):
LOG.debug(_("Downloaded image %s from glance image server") % image)
-def _get_s3_image(image, instance, **kwargs):
- """Download image from the S3 image server."""
- raise NotImplementedError
-
-
-def _get_local_image(image, instance, **kwargs):
- """Download image from the local nova compute node."""
- raise NotImplementedError
-
-
-def _put_glance_image(image, instance, **kwargs):
+def upload_image(context, image, instance, **kwargs):
"""Upload the snapshotted vm disk file to Glance image server."""
LOG.debug(_("Uploading image %s to the Glance image server") % image)
read_file_handle = read_write_util.VmWareHTTPReadFile(
@@ -152,7 +114,7 @@ def _put_glance_image(image, instance, **kwargs):
kwargs.get("cookies"),
kwargs.get("file_path"))
file_size = read_file_handle.get_size()
- (glance_client, image_id) = nova.image.get_glance_client(image)
+ (glance_client, image_id) = glance.get_glance_client(context, image)
# The properties and other fields that we need to set for the image.
image_metadata = {"is_public": True,
"disk_format": "vmdk",
@@ -168,17 +130,7 @@ def _put_glance_image(image, instance, **kwargs):
LOG.debug(_("Uploaded image %s to the Glance image server") % image)
-def _put_local_image(image, instance, **kwargs):
- """Upload the snapshotted vm disk file to the local nova compute node."""
- raise NotImplementedError
-
-
-def _put_s3_image(image, instance, **kwargs):
- """Upload the snapshotted vm disk file to S3 image server."""
- raise NotImplementedError
-
-
-def get_vmdk_size_and_properties(image, instance):
+def get_vmdk_size_and_properties(context, image, instance):
"""
Get size of the vmdk file that is to be downloaded for attach in spawn.
Need this to create the dummy virtual disk for the meta-data file. The
@@ -186,12 +138,9 @@ def get_vmdk_size_and_properties(image, instance):
"""
LOG.debug(_("Getting image size for the image %s") % image)
- if FLAGS.image_service == "nova.image.glance.GlanceImageService":
- (glance_client, image_id) = nova.image.get_glance_client(image)
- meta_data = glance_client.get_image_meta(image_id)
- size, properties = meta_data["size"], meta_data["properties"]
- elif FLAGS.image_service == "nova.image.s3.S3ImageService":
- raise NotImplementedError
+ (glance_client, image_id) = glance.get_glance_client(context, image)
+ meta_data = glance_client.get_image_meta(image_id)
+ size, properties = meta_data["size"], meta_data["properties"]
LOG.debug(_("Got image size of %(size)s for the image %(image)s") %
locals())
return size, properties
diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py
index 243ee64f5..fa89a8f45 100644
--- a/nova/virt/vmwareapi_conn.py
+++ b/nova/virt/vmwareapi_conn.py
@@ -133,7 +133,7 @@ class VMWareESXConnection(driver.ComputeDriver):
"""Create snapshot from a running VM instance."""
self._vmops.snapshot(context, instance, name)
- def reboot(self, instance, network_info):
+ def reboot(self, instance, network_info, reboot_type):
"""Reboot VM instance."""
self._vmops.reboot(instance, network_info)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index efbea7076..302238c98 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -31,12 +31,10 @@ import urllib
import uuid
from xml.dom import minidom
-import glance.client
from nova import db
from nova import exception
from nova import flags
-import nova.image
-from nova.image import glance as glance_image_service
+from nova.image import glance
from nova import log as logging
from nova import utils
from nova.compute import instance_types
@@ -383,8 +381,7 @@ class VMHelper(HelperBase):
os_type = instance.os_type or FLAGS.default_os_type
- glance_host, glance_port = \
- glance_image_service.pick_glance_api_server()
+ glance_host, glance_port = glance.pick_glance_api_server()
params = {'vdi_uuids': vdi_uuids,
'image_id': image_id,
'glance_host': glance_host,
@@ -447,8 +444,7 @@ class VMHelper(HelperBase):
# pass them as arguments
uuid_stack = [str(uuid.uuid4()) for i in xrange(2)]
- glance_host, glance_port = \
- glance_image_service.pick_glance_api_server()
+ glance_host, glance_port = glance.pick_glance_api_server()
params = {'image_id': image,
'glance_host': glance_host,
'glance_port': glance_port,
@@ -546,7 +542,7 @@ class VMHelper(HelperBase):
else:
sr_ref = safe_find_sr(session)
- glance_client, image_id = nova.image.get_glance_client(image)
+ glance_client, image_id = glance.get_glance_client(context, image)
glance_client.set_auth_token(getattr(context, 'auth_token', None))
meta, image_file = glance_client.get_image(image_id)
virtual_size = int(meta['size'])
@@ -606,7 +602,7 @@ class VMHelper(HelperBase):
raise e
@classmethod
- def determine_disk_image_type(cls, instance):
+ def determine_disk_image_type(cls, instance, context):
"""Disk Image Types are used to determine where the kernel will reside
within an image. To figure out which type we're dealing with, we use
the following rules:
@@ -639,7 +635,8 @@ class VMHelper(HelperBase):
'vhd': ImageType.DISK_VHD,
'iso': ImageType.DISK_ISO}
image_ref = instance.image_ref
- glance_client, image_id = nova.image.get_glance_client(image_ref)
+ glance_client, image_id = glance.get_glance_client(context,
+ image_ref)
meta = glance_client.get_image_meta(image_id)
disk_format = meta['disk_format']
try:
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 071ab38d3..5ba62d63e 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -135,7 +135,7 @@ class VMOps(object):
self._session.call_xenapi('VM.start', vm_ref, False, False)
def _create_disks(self, context, instance):
- disk_image_type = VMHelper.determine_disk_image_type(instance)
+ disk_image_type = VMHelper.determine_disk_image_type(instance, context)
vdis = VMHelper.fetch_image(context, self._session,
instance, instance.image_ref,
instance.user_id, instance.project_id,
@@ -176,7 +176,7 @@ class VMOps(object):
power_state.SHUTDOWN)
return
- disk_image_type = VMHelper.determine_disk_image_type(instance)
+ disk_image_type = VMHelper.determine_disk_image_type(instance, context)
kernel = None
ramdisk = None
try:
@@ -624,10 +624,15 @@ class VMOps(object):
str(new_disk_size))
LOG.debug(_("Resize instance %s complete") % (instance.name))
- def reboot(self, instance):
+ def reboot(self, instance, reboot_type):
"""Reboot VM instance."""
vm_ref = self._get_vm_opaque_ref(instance)
- task = self._session.call_xenapi('Async.VM.clean_reboot', vm_ref)
+
+ if reboot_type == "HARD":
+ task = self._session.call_xenapi('Async.VM.hard_reboot', vm_ref)
+ else:
+ task = self._session.call_xenapi('Async.VM.clean_reboot', vm_ref)
+
self._session.wait_for_task(task, instance.id)
def get_agent_version(self, instance, timeout=None):
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 69a17f721..46fdc107d 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -203,9 +203,9 @@ class XenAPIConnection(driver.ComputeDriver):
""" Create snapshot from a running VM instance """
self._vmops.snapshot(context, instance, image_id)
- def reboot(self, instance, network_info):
+ def reboot(self, instance, network_info, reboot_type):
"""Reboot VM instance"""
- self._vmops.reboot(instance)
+ self._vmops.reboot(instance, reboot_type)
def set_admin_password(self, instance, new_pass):
"""Set the root/admin password on the VM instance"""
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index a06312890..1a9ac37e9 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -252,7 +252,11 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port, os_type,
# NOTE(dprince): We need to resend any existing Glance meta/property
# headers so they are preserved in Glance. We obtain them here with a
# HEAD request.
- conn.request('HEAD', '/v1/images/%s' % image_id)
+ conn.putrequest('HEAD', '/v1/images/%s' % image_id)
+ if auth_token:
+ conn.putheader('x-auth-token', auth_token)
+ conn.endheaders()
+
resp = conn.getresponse()
if resp.status != httplib.OK:
raise Exception("Unexpected response from Glance %i" % resp.status)