summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Moser <smoser@ubuntu.com>2011-09-20 03:23:34 -0400
committerScott Moser <smoser@ubuntu.com>2011-09-20 03:23:34 -0400
commit2eb2120f98cfe70ce67325ffe26cfb5cc86c6356 (patch)
tree8b2f431f158878864fa5b44abfc035270a834513
parentb399ac7b6e80d16fddd61c9b2d505cff09cb8889 (diff)
parentdcd646e6610e2a5cc6da78220ab0c8acde48d401 (diff)
downloadnova-2eb2120f98cfe70ce67325ffe26cfb5cc86c6356.tar.gz
nova-2eb2120f98cfe70ce67325ffe26cfb5cc86c6356.tar.xz
nova-2eb2120f98cfe70ce67325ffe26cfb5cc86c6356.zip
merge with trunk r1601
-rw-r--r--Authors1
-rwxr-xr-xbin/nova-manage66
-rw-r--r--nova/compute/api.py5
-rw-r--r--nova/compute/manager.py16
-rw-r--r--nova/db/api.py18
-rw-r--r--nova/db/sqlalchemy/api.py62
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py32
-rwxr-xr-xnova/network/linux_net.py1
-rw-r--r--nova/network/manager.py9
-rw-r--r--nova/tests/fake_network.py8
-rw-r--r--nova/tests/test_compute.py4
-rw-r--r--nova/tests/test_db_api.py34
-rw-r--r--nova/tests/test_virt_drivers.py4
-rw-r--r--nova/virt/driver.py5
-rw-r--r--nova/virt/fake.py3
-rw-r--r--nova/virt/hyperv.py3
-rw-r--r--nova/virt/libvirt/connection.py4
-rw-r--r--nova/virt/xenapi/vmops.py23
-rw-r--r--nova/virt/xenapi_conn.py4
19 files changed, 214 insertions, 88 deletions
diff --git a/Authors b/Authors
index 4e0848692..8d6837ea4 100644
--- a/Authors
+++ b/Authors
@@ -12,6 +12,7 @@ Armando Migliaccio <Armando.Migliaccio@eu.citrix.com>
Arvind Somya <asomya@cisco.com>
Bilal Akhtar <bilalakhtar@ubuntu.com>
Brad Hall <brad@nicira.com>
+Brad McConnell <bmcconne@rackspace.com>
Brian Lamar <brian.lamar@rackspace.com>
Brian Schott <bschott@isi.edu>
Brian Waldon <brian.waldon@rackspace.com>
diff --git a/bin/nova-manage b/bin/nova-manage
index 089b2eeae..1f4aa8a78 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -61,6 +61,7 @@ import math
import netaddr
from optparse import OptionParser
import os
+import StringIO
import sys
import time
@@ -274,6 +275,58 @@ class ShellCommands(object):
arguments: path"""
exec(compile(open(path).read(), path, 'exec'), locals(), globals())
+ @args('--filename', dest='filename', metavar='<path>', default=False,
+ help='Export file path')
+ def export(self, filename):
+ """Export Nova users into a file that can be consumed by Keystone"""
+
+ def create_file(filename):
+ data = generate_data()
+ with open(filename, 'w') as f:
+ f.write(data.getvalue())
+
+ def tenants(data, am):
+ for project in am.get_projects():
+ print >> data, ("tenant add '%s'" %
+ (project.name))
+ for u in project.member_ids:
+ user = am.get_user(u)
+ print >> data, ("user add '%s' '%s' '%s'" %
+ (user.name, user.access, project.name))
+ print >> data, ("credentials add 'EC2' '%s:%s' '%s' '%s'" %
+ (user.access, project.id, user.secret, project.id))
+
+ def roles(data, am):
+ for role in am.get_roles():
+ print >> data, ("role add '%s'" % (role))
+
+ def grant_roles(data, am):
+ roles = am.get_roles()
+ for project in am.get_projects():
+ for u in project.member_ids:
+ user = am.get_user(u)
+ for role in db.user_get_roles_for_project(ctxt, u,
+ project.id):
+ print >> data, ("role grant '%s', '%s', '%s')," %
+ (user.name, role, project.name))
+ print >> data
+
+ def generate_data():
+ data = StringIO.StringIO()
+ am = manager.AuthManager()
+ tenants(data, am)
+ roles(data, am)
+ grant_roles(data, am)
+ data.seek(0)
+ return data
+
+ ctxt = context.get_admin_context()
+ if filename:
+ create_file(filename)
+ else:
+ data = generate_data()
+ print data.getvalue()
+
class RoleCommands(object):
"""Class for managing roles."""
@@ -685,7 +738,7 @@ class NetworkCommands(object):
help='Multi host')
@args('--dns1', dest="dns1", metavar="<DNS Address>", help='First DNS')
@args('--dns2', dest="dns2", metavar="<DNS Address>", help='Second DNS')
- @args('--uuid', dest="net_uuid", metavar="<network uuid>",
+ @args('--uuid', dest="uuid", metavar="<network uuid>",
help='Network UUID')
@args('--project_id', dest="project_id", metavar="<project id>",
help='Project id')
@@ -710,16 +763,7 @@ class NetworkCommands(object):
bridge_required = ['nova.network.manager.FlatManager',
'nova.network.manager.FlatDHCPManager']
if FLAGS.network_manager in bridge_required:
- # TODO(tr3buchet) - swap print statement and following line for
- # raise statement in diablo 4
- print _('--bridge parameter required or FLAG '
- 'flat_network_bridge must be set to create networks\n'
- 'WARNING! ACHTUNG! Setting the bridge to br100 '
- 'automatically is deprecated and will be removed in '
- 'Diablo milestone 4. Prepare yourself accordingly.')
- time.sleep(10)
- bridge = 'br100'
- #raise exception.NetworkNotCreated(req='--bridge')
+ raise exception.NetworkNotCreated(req='--bridge')
bridge_interface = bridge_interface or FLAGS.flat_interface or \
FLAGS.vlan_interface
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 853e6ef9e..76e1e7a60 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -937,9 +937,10 @@ class API(base.Base):
filters)
# NOTE(jkoelker) It is possible that we will get the same
# instance uuid twice (one for ipv4 and ipv6)
- ids = set([r['instance_uuid'] for r in res])
+ uuids = set([r['instance_uuid'] for r in res])
+ filters['uuid'] = uuids
- return self.db.instance_get_all_by_filters(context, filters, ids)
+ return self.db.instance_get_all_by_filters(context, filters)
def _cast_compute_message(self, method, context, instance_id, host=None,
params=None):
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 46c643aee..cb5d10f83 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -79,6 +79,9 @@ flags.DEFINE_integer('live_migration_retry_count', 30,
flags.DEFINE_integer("rescue_timeout", 0,
"Automatically unrescue an instance after N seconds."
" Set to 0 to disable.")
+flags.DEFINE_integer("resize_confirm_window", 0,
+ "Automatically confirm resizes after N seconds."
+ " Set to 0 to disable.")
flags.DEFINE_integer('host_state_interval', 120,
'Interval in seconds for querying the host status')
@@ -1648,14 +1651,23 @@ class ComputeManager(manager.SchedulerDependentManager):
self.driver.poll_rescued_instances(FLAGS.rescue_timeout)
except Exception as ex:
LOG.warning(_("Error during poll_rescued_instances: %s"),
- unicode(ex))
+ unicode(ex))
+ error_list.append(ex)
+
+ try:
+ if FLAGS.resize_confirm_window > 0:
+ self.driver.poll_unconfirmed_resizes(
+ FLAGS.resize_confirm_window)
+ except Exception as ex:
+ LOG.warning(_("Error during poll_unconfirmed_resizes: %s"),
+ unicode(ex))
error_list.append(ex)
try:
self._report_driver_status()
except Exception as ex:
LOG.warning(_("Error during report_driver_status(): %s"),
- unicode(ex))
+ unicode(ex))
error_list.append(ex)
try:
diff --git a/nova/db/api.py b/nova/db/api.py
index f68e1c48e..8c4c78374 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -323,6 +323,11 @@ def migration_get_by_instance_and_status(context, instance_uuid, status):
status)
+def migration_get_all_unconfirmed(context, confirm_window):
+ """Finds all unconfirmed migrations within the confirmation window."""
+ return IMPL.migration_get_all_unconfirmed(context, confirm_window)
+
+
####################
@@ -463,7 +468,7 @@ def virtual_interface_delete_by_instance(context, instance_id):
def virtual_interface_get_all(context):
- """Gets all virtual interfaces from the table,"""
+ """Gets all virtual interfaces from the table"""
return IMPL.virtual_interface_get_all(context)
@@ -505,10 +510,9 @@ def instance_get_all(context):
return IMPL.instance_get_all(context)
-def instance_get_all_by_filters(context, filters, instance_uuids=None):
+def instance_get_all_by_filters(context, filters):
"""Get all instances that match all filters."""
- return IMPL.instance_get_all_by_filters(context, filters,
- instance_uuids=instance_uuids)
+ return IMPL.instance_get_all_by_filters(context, filters)
def instance_get_active_by_window(context, begin, end=None, project_id=None):
@@ -612,9 +616,9 @@ def instance_get_actions(context, instance_id):
return IMPL.instance_get_actions(context, instance_id)
-def instance_get_uuids_by_ids(context, ids):
- """Return the UUIDs of the instances given the ids"""
- return IMPL.instance_get_uuids_by_ids(context, ids)
+def instance_get_id_to_uuid_mapping(context, ids):
+ """Return a dictionary containing 'ID: UUID' given the ids"""
+ return IMPL.instance_get_id_to_uuid_mapping(context, ids)
###################
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index c4bff8308..d8b5ef5c2 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -15,9 +15,10 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-"""
-Implementation of SQLAlchemy backend.
-"""
+
+"""Implementation of SQLAlchemy backend."""
+
+import datetime
import re
import warnings
@@ -1203,7 +1204,7 @@ def instance_get_all(context):
@require_context
-def instance_get_all_by_filters(context, filters, instance_uuids=None):
+def instance_get_all_by_filters(context, filters):
"""Return instances that match all filters. Deleted instances
will be returned by default, unless there's a filter that says
otherwise"""
@@ -1234,7 +1235,7 @@ def instance_get_all_by_filters(context, filters, instance_uuids=None):
"""Do exact match against a column. value to match can be a list
so you can match any value in the list.
"""
- if isinstance(value, list):
+ if isinstance(value, list) or isinstance(value, set):
column_attr = getattr(models.Instance, column)
return query.filter(column_attr.in_(value))
else:
@@ -1268,7 +1269,7 @@ def instance_get_all_by_filters(context, filters, instance_uuids=None):
# Filters for exact matches that we can do along with the SQL query...
# For other filters that don't match this, we will do regexp matching
exact_match_filter_names = ['project_id', 'user_id', 'image_ref',
- 'vm_state', 'instance_type_id', 'deleted']
+ 'vm_state', 'instance_type_id', 'deleted', 'uuid']
query_filters = [key for key in filters.iterkeys()
if key in exact_match_filter_names]
@@ -1279,30 +1280,10 @@ def instance_get_all_by_filters(context, filters, instance_uuids=None):
query_prefix = _exact_match_filter(query_prefix, filter_name,
filters.pop(filter_name))
- db_instances = query_prefix.all()
- db_uuids = [instance['uuid'] for instance in db_instances \
- if instance['uuid']]
-
- if instance_uuids is None:
- instance_uuids = []
-
- uuids = []
-
- # NOTE(jkoelker): String UUIDs only!
- if not instance_uuids:
- uuids = db_uuids
- elif not db_instances:
- uuids = instance_uuids
- else:
- uuids = list(set(instance_uuids) & set(db_uuids))
-
- if not uuids:
+ instances = query_prefix.all()
+ if not instances:
return []
- instances = session.query(models.Instance).\
- filter(models.Instance.uuid.in_(uuids)).\
- all()
-
# Now filter on everything else for regexp matching..
# For filters not in the list, we'll attempt to use the filter_name
# as a column name in Instance..
@@ -1320,6 +1301,8 @@ def instance_get_all_by_filters(context, filters, instance_uuids=None):
filter_l = lambda instance: _regexp_filter_by_column(instance,
filter_name, filter_re)
instances = filter(filter_l, instances)
+ if not instances:
+ break
return instances
@@ -1564,13 +1547,15 @@ def instance_get_actions(context, instance_id):
@require_context
-def instance_get_uuids_by_ids(context, ids):
+def instance_get_id_to_uuid_mapping(context, ids):
session = get_session()
instances = session.query(models.Instance).\
filter(models.Instance.id.in_(ids)).\
all()
- return [{'uuid': instance['uuid'],
- 'id': instance['id']} for instance in instances]
+ mapping = {}
+ for instance in instances:
+ mapping[instance['id']] = instance['uuid']
+ return mapping
###################
@@ -3170,6 +3155,21 @@ def migration_get_by_instance_and_status(context, instance_uuid, status):
return result
+@require_admin_context
+def migration_get_all_unconfirmed(context, confirm_window, session=None):
+ confirm_window = datetime.datetime.utcnow() - datetime.timedelta(
+ seconds=confirm_window)
+
+ if not session:
+ session = get_session()
+
+ results = session.query(models.Migration).\
+ filter(models.Migration.updated_at <= confirm_window).\
+ filter_by(status="FINISHED").all()
+
+ return results
+
+
##################
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py b/nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py
index 818925c32..dadcefc39 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py
@@ -17,21 +17,9 @@ from migrate import ForeignKeyConstraint
from nova import log as logging
-
meta = MetaData()
-instances = Table('instances', meta,
- Column('id', Integer(), primary_key=True, nullable=False),
- )
-
-
-vifs = Table('virtual_interfaces', meta,
- Column('id', Integer(), primary_key=True, nullable=False),
- Column('instance_id', Integer()),
- )
-
-
def upgrade(migrate_engine):
# Upgrade operations go here. Don't create your own engine;
# bind migrate_engine to your metadata
@@ -40,17 +28,18 @@ def upgrade(migrate_engine):
if dialect.startswith('sqlite'):
return
+ instances = Table('instances', meta, autoload=True)
+ vifs = Table('virtual_interfaces', meta, autoload=True)
+
try:
+ fkey_name = vifs.c.instance_id.foreign_keys[0].constraint.name
ForeignKeyConstraint(columns=[vifs.c.instance_id],
- refcolumns=[instances.c.id]).drop()
+ refcolumns=[instances.c.id],
+ name=fkey_name).drop()
+
except Exception:
- try:
- migrate_engine.execute("ALTER TABLE migrations DROP " \
- "FOREIGN KEY " \
- "`virtual_interfaces_ibfk_2`;")
- except Exception:
- logging.error(_("foreign key constraint couldn't be removed"))
- raise
+ logging.error(_("foreign key constraint couldn't be removed"))
+ raise
def downgrade(migrate_engine):
@@ -60,6 +49,9 @@ def downgrade(migrate_engine):
if dialect.startswith('sqlite'):
return
+ instances = Table('instances', meta, autoload=True)
+ vifs = Table('virtual_interfaces', meta, autoload=True)
+
try:
ForeignKeyConstraint(columns=[vifs.c.instance_id],
refcolumns=[instances.c.id]).create()
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 2f9805fa3..9bf98fc27 100755
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -824,7 +824,6 @@ class LinuxNetInterfaceDriver(object):
raise NotImplementedError()
-
# plugs interfaces using Linux Bridge
class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 2687d8faf..ffb9f976c 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -410,7 +410,7 @@ class NetworkManager(manager.SchedulerDependentManager):
ip_filter = re.compile(str(filters.get('ip')))
ipv6_filter = re.compile(str(filters.get('ip6')))
- # NOTE(jkoelker) Should probably figur out a better way to do
+ # NOTE(jkoelker) Should probably figure out a better way to do
# this. But for now it "works", this could suck on
# large installs.
@@ -448,12 +448,9 @@ class NetworkManager(manager.SchedulerDependentManager):
# NOTE(jkoelker) Until we switch over to instance_uuid ;)
ids = [res['instance_id'] for res in results]
- uuids = self.db.instance_get_uuids_by_ids(context, ids)
+ uuid_map = self.db.instance_get_id_to_uuid_mapping(context, ids)
for res in results:
- uuid = [u['uuid'] for u in uuids if u['id'] == res['instance_id']]
- # NOTE(jkoelker) UUID must exist, so no test here
- res['instance_uuid'] = uuid[0]
-
+ res['instance_uuid'] = uuid_map.get(res['instance_id'])
return results
def _get_networks_for_instance(self, context, instance_id, project_id,
diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py
index d839f20b0..febac5e09 100644
--- a/nova/tests/fake_network.py
+++ b/nova/tests/fake_network.py
@@ -105,10 +105,12 @@ class FakeNetworkManager(network_manager.NetworkManager):
'floating_ips': [floats[2]]}]}]
return vifs
- def instance_get_uuids_by_ids(self, context, ids):
+ def instance_get_id_to_uuid_mapping(self, context, ids):
# NOTE(jkoelker): This is just here until we can rely on UUIDs
- return [{'uuid': str(utils.gen_uuid()),
- 'id': id} for id in ids]
+ mapping = {}
+ for id in ids:
+ mapping[id] = str(utils.gen_uuid())
+ return mapping
def __init__(self):
self.db = self.FakeDB()
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 4ef57b760..948c7ad40 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -1033,8 +1033,8 @@ class ComputeTestCase(test.TestCase):
'get_instance_uuids_by_ip_filter',
network_manager.get_instance_uuids_by_ip_filter)
self.stubs.Set(network_manager.db,
- 'instance_get_uuids_by_ids',
- db.instance_get_uuids_by_ids)
+ 'instance_get_id_to_uuid_mapping',
+ db.instance_get_id_to_uuid_mapping)
instance_id1 = self._create_instance({'display_name': 'woot',
'id': 0})
diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py
index 5ebab9cc8..81194e3f9 100644
--- a/nova/tests/test_db_api.py
+++ b/nova/tests/test_db_api.py
@@ -18,6 +18,8 @@
"""Unit tests for the DB API"""
+import datetime
+
from nova import test
from nova import context
from nova import db
@@ -92,6 +94,32 @@ class DbApiTestCase(test.TestCase):
db.instance_destroy(self.context, inst1.id)
result = db.instance_get_all_by_filters(self.context.elevated(), {})
self.assertEqual(2, len(result))
- self.assertEqual(result[0].id, inst1.id)
- self.assertEqual(result[1].id, inst2.id)
- self.assertTrue(result[0].deleted)
+ self.assertIn(inst1.id, [result[0].id, result[1].id])
+ self.assertIn(inst2.id, [result[0].id, result[1].id])
+ if inst1.id == result[0].id:
+ self.assertTrue(result[0].deleted)
+ else:
+ self.assertTrue(result[1].deleted)
+
+ def test_migration_get_all_unconfirmed(self):
+ ctxt = context.get_admin_context()
+
+ # Ensure no migrations are returned.
+ results = db.migration_get_all_unconfirmed(ctxt, 10)
+ self.assertEqual(0, len(results))
+
+ # Ensure one migration older than 10 seconds is returned.
+ updated_at = datetime.datetime(2000, 01, 01, 12, 00, 00)
+ values = {"status": "FINISHED", "updated_at": updated_at}
+ migration = db.migration_create(ctxt, values)
+ results = db.migration_get_all_unconfirmed(ctxt, 10)
+ self.assertEqual(1, len(results))
+ db.migration_update(ctxt, migration.id, {"status": "CONFIRMED"})
+
+ # Ensure the new migration is not returned.
+ updated_at = datetime.datetime.utcnow()
+ values = {"status": "FINISHED", "updated_at": updated_at}
+ migration = db.migration_create(ctxt, values)
+ results = db.migration_get_all_unconfirmed(ctxt, 10)
+ self.assertEqual(0, len(results))
+ db.migration_update(ctxt, migration.id, {"status": "CONFIRMED"})
diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py
index 440d3401b..8e20e999f 100644
--- a/nova/tests/test_virt_drivers.py
+++ b/nova/tests/test_virt_drivers.py
@@ -177,6 +177,10 @@ class _VirtDriverTestCase(test.TestCase):
self.connection.poll_rescued_instances(10)
@catch_notimplementederror
+ def test_poll_unconfirmed_resizes(self):
+ self.connection.poll_unconfirmed_resizes(10)
+
+ @catch_notimplementederror
def test_migrate_disk_and_power_off(self):
instance_ref = test_utils.get_test_instance()
network_info = test_utils.get_test_network_info()
diff --git a/nova/virt/driver.py b/nova/virt/driver.py
index 301346c6b..fc47d8d2d 100644
--- a/nova/virt/driver.py
+++ b/nova/virt/driver.py
@@ -469,6 +469,11 @@ class ComputeDriver(object):
# TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()
+ def poll_unconfirmed_resizes(self, resize_confirm_window):
+ """Poll for unconfirmed resizes"""
+ # TODO(Vek): Need to pass context in for access to auth_token
+ raise NotImplementedError()
+
def host_power_action(self, host, action):
"""Reboots, shuts down or powers up the host."""
raise NotImplementedError()
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index 3596d8353..96f521ee7 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -130,6 +130,9 @@ class FakeConnection(driver.ComputeDriver):
def poll_rescued_instances(self, timeout):
pass
+ def poll_unconfirmed_resizes(self, resize_confirm_window):
+ pass
+
def migrate_disk_and_power_off(self, instance, dest):
pass
diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py
index 76925b405..fbf898317 100644
--- a/nova/virt/hyperv.py
+++ b/nova/virt/hyperv.py
@@ -487,6 +487,9 @@ class HyperVConnection(driver.ComputeDriver):
def poll_rescued_instances(self, timeout):
pass
+ def poll_unconfirmed_resizes(self, resize_confirm_window):
+ pass
+
def update_available_resource(self, ctxt, host):
"""This method is supported only by libvirt."""
return
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index 44a733340..2a38691c8 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -588,6 +588,10 @@ class LibvirtConnection(driver.ComputeDriver):
def poll_rescued_instances(self, timeout):
pass
+ @exception.wrap_exception()
+ def poll_unconfirmed_resizes(self, resize_confirm_window):
+ pass
+
# NOTE(ilyaalekseyev): Implementation like in multinics
# for xenapi(tr3buchet)
@exception.wrap_exception()
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 8f7eb3795..210b8fe65 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -38,6 +38,7 @@ from nova import ipv6
from nova import log as logging
from nova import utils
+from nova.compute import api as compute
from nova.compute import power_state
from nova.virt import driver
from nova.virt.xenapi.network_utils import NetworkHelper
@@ -77,6 +78,7 @@ class VMOps(object):
"""
def __init__(self, session):
self.XenAPI = session.get_imported_xenapi()
+ self.compute_api = compute.API()
self._session = session
self.poll_rescue_last_ran = None
VMHelper.XenAPI = self.XenAPI
@@ -1039,6 +1041,27 @@ class VMOps(object):
self._session.call_xenapi("VM.start", original_vm_ref, False,
False)
+ def poll_unconfirmed_resizes(self, resize_confirm_window):
+ """Poll for unconfirmed resizes.
+
+ Look for any unconfirmed resizes that are older than
+ `resize_confirm_window` and automatically confirm them.
+ """
+ ctxt = nova_context.get_admin_context()
+ migrations = db.migration_get_all_unconfirmed(ctxt,
+ resize_confirm_window)
+
+ migrations_info = dict(migration_count=len(migrations),
+ confirm_window=FLAGS.resize_confirm_window)
+
+ if migrations_info["migration_count"] > 0:
+ LOG.info(_("Found %(migration_count)d unconfirmed migrations "
+ "older than %(confirm_window)d seconds") % migrations_info)
+
+ for migration in migrations:
+ LOG.info(_("Automatically confirming migration %d"), migration.id)
+ self.compute_api.confirm_resize(ctxt, migration.instance_uuid)
+
def get_info(self, instance):
"""Return data about VM instance."""
vm_ref = self._get_vm_opaque_ref(instance)
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index f6dbc19f8..7fc683a9f 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -254,6 +254,10 @@ class XenAPIConnection(driver.ComputeDriver):
"""Poll for rescued instances"""
self._vmops.poll_rescued_instances(timeout)
+ def poll_unconfirmed_resizes(self, resize_confirm_window):
+ """Poll for unconfirmed resizes"""
+ self._vmops.poll_unconfirmed_resizes(resize_confirm_window)
+
def reset_network(self, instance):
"""reset networking for specified instance"""
self._vmops.reset_network(instance)