summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Wolf <throughnothing@gmail.com>2011-09-19 22:09:53 -0400
committerWilliam Wolf <throughnothing@gmail.com>2011-09-19 22:09:53 -0400
commitf27ed550fae070c8cfeaf638b2a00a9b80f188c1 (patch)
tree7b8afa180e5f2ada4c8bf35c340fcc043b185f98
parenta0e705359353cb6a9b0c3fc8178e714e4350c585 (diff)
parentb90503021b5d55c51cf082fd168512bd4586383d (diff)
downloadnova-f27ed550fae070c8cfeaf638b2a00a9b80f188c1.tar.gz
nova-f27ed550fae070c8cfeaf638b2a00a9b80f188c1.tar.xz
nova-f27ed550fae070c8cfeaf638b2a00a9b80f188c1.zip
merged with trunk
-rwxr-xr-xbin/nova-manage53
-rw-r--r--nova/api/openstack/contrib/virtual_interfaces.py16
-rw-r--r--nova/api/openstack/servers.py6
-rw-r--r--nova/compute/api.py14
-rw-r--r--nova/compute/manager.py16
-rw-r--r--nova/db/api.py15
-rw-r--r--nova/db/sqlalchemy/api.py123
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py68
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/047_sqlite_downgrade.sql47
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/047_sqlite_upgrade.sql45
-rw-r--r--nova/db/sqlalchemy/models.py5
-rw-r--r--nova/network/api.py14
-rwxr-xr-xnova/network/linux_net.py19
-rw-r--r--nova/network/manager.py61
-rw-r--r--nova/tests/api/openstack/contrib/test_virtual_interfaces.py17
-rw-r--r--nova/tests/api/openstack/test_servers.py54
-rw-r--r--nova/tests/fake_network.py61
-rw-r--r--nova/tests/test_compute.py253
-rw-r--r--nova/tests/test_db_api.py34
-rw-r--r--nova/tests/test_metadata.py9
-rw-r--r--nova/tests/test_network.py182
-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
28 files changed, 763 insertions, 395 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index 089b2eeae..4e9307273 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."""
diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py
index dab61efc8..1981cd372 100644
--- a/nova/api/openstack/contrib/virtual_interfaces.py
+++ b/nova/api/openstack/contrib/virtual_interfaces.py
@@ -15,15 +15,10 @@
"""The virtual interfaces extension."""
-from webob import exc
-import webob
-
-from nova import compute
-from nova import exception
from nova import log as logging
+from nova import network
from nova.api.openstack import common
from nova.api.openstack import extensions
-from nova.api.openstack import faults
from nova.api.openstack import wsgi
@@ -50,19 +45,14 @@ class ServerVirtualInterfaceController(object):
"""
def __init__(self):
- self.compute_api = compute.API()
+ self.network_api = network.API()
super(ServerVirtualInterfaceController, self).__init__()
def _items(self, req, server_id, entity_maker):
"""Returns a list of VIFs, transformed through entity_maker."""
context = req.environ['nova.context']
- try:
- instance = self.compute_api.get(context, server_id)
- except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
-
- vifs = instance['virtual_interfaces']
+ vifs = self.network_api.get_vifs_by_instance(context, server_id)
limited_list = common.limited(vifs, req)
res = [entity_maker(context, vif) for vif in limited_list]
return {'virtual_interfaces': res}
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index ac6b4918a..211925bd0 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -661,7 +661,11 @@ class ControllerV11(Controller):
def _get_key_name(self, req, body):
if 'server' in body:
- return body['server'].get('key_name')
+ try:
+ return body['server'].get('key_name')
+ except AttributeError:
+ msg = _("Malformed server entity")
+ raise exc.HTTPBadRequest(explanation=msg)
def _image_ref_from_req_data(self, data):
try:
diff --git a/nova/compute/api.py b/nova/compute/api.py
index d8657d403..76e1e7a60 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -905,7 +905,7 @@ class API(base.Base):
if 'reservation_id' in filters:
recurse_zones = True
- instances = self.db.instance_get_all_by_filters(context, filters)
+ instances = self._get_instances_by_filters(context, filters)
if not recurse_zones:
return instances
@@ -930,6 +930,18 @@ class API(base.Base):
return instances
+ def _get_instances_by_filters(self, context, filters):
+ ids = None
+ if 'ip6' in filters or 'ip' in filters:
+ res = self.network_api.get_instance_uuids_by_ip_filter(context,
+ filters)
+ # NOTE(jkoelker) It is possible that we will get the same
+ # instance uuid twice (one for ipv4 and ipv6)
+ uuids = set([r['instance_uuid'] for r in res])
+ filters['uuid'] = uuids
+
+ return self.db.instance_get_all_by_filters(context, filters)
+
def _cast_compute_message(self, method, context, instance_id, host=None,
params=None):
"""Generic handler for RPC casts to compute.
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 05d81d8b2..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)
+
+
####################
@@ -462,6 +467,11 @@ def virtual_interface_delete_by_instance(context, instance_id):
return IMPL.virtual_interface_delete_by_instance(context, instance_id)
+def virtual_interface_get_all(context):
+ """Gets all virtual interfaces from the table"""
+ return IMPL.virtual_interface_get_all(context)
+
+
####################
@@ -606,6 +616,11 @@ def instance_get_actions(context, instance_id):
return IMPL.instance_get_actions(context, instance_id)
+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 5889f5fc3..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
@@ -930,7 +931,6 @@ def virtual_interface_get(context, vif_id, session=None):
vif_ref = session.query(models.VirtualInterface).\
filter_by(id=vif_id).\
options(joinedload('network')).\
- options(joinedload('instance')).\
options(joinedload('fixed_ips')).\
first()
return vif_ref
@@ -946,7 +946,6 @@ def virtual_interface_get_by_address(context, address):
vif_ref = session.query(models.VirtualInterface).\
filter_by(address=address).\
options(joinedload('network')).\
- options(joinedload('instance')).\
options(joinedload('fixed_ips')).\
first()
return vif_ref
@@ -962,7 +961,6 @@ def virtual_interface_get_by_uuid(context, vif_uuid):
vif_ref = session.query(models.VirtualInterface).\
filter_by(uuid=vif_uuid).\
options(joinedload('network')).\
- options(joinedload('instance')).\
options(joinedload('fixed_ips')).\
first()
return vif_ref
@@ -978,7 +976,6 @@ def virtual_interface_get_by_fixed_ip(context, fixed_ip_id):
vif_ref = session.query(models.VirtualInterface).\
filter_by(fixed_ip_id=fixed_ip_id).\
options(joinedload('network')).\
- options(joinedload('instance')).\
options(joinedload('fixed_ips')).\
first()
return vif_ref
@@ -995,7 +992,6 @@ def virtual_interface_get_by_instance(context, instance_id):
vif_refs = session.query(models.VirtualInterface).\
filter_by(instance_id=instance_id).\
options(joinedload('network')).\
- options(joinedload('instance')).\
options(joinedload('fixed_ips')).\
all()
return vif_refs
@@ -1010,7 +1006,6 @@ def virtual_interface_get_by_instance_and_network(context, instance_id,
filter_by(instance_id=instance_id).\
filter_by(network_id=network_id).\
options(joinedload('network')).\
- options(joinedload('instance')).\
options(joinedload('fixed_ips')).\
first()
return vif_ref
@@ -1026,7 +1021,6 @@ def virtual_interface_get_by_network(context, network_id):
vif_refs = session.query(models.VirtualInterface).\
filter_by(network_id=network_id).\
options(joinedload('network')).\
- options(joinedload('instance')).\
options(joinedload('fixed_ips')).\
all()
return vif_refs
@@ -1056,6 +1050,17 @@ def virtual_interface_delete_by_instance(context, instance_id):
virtual_interface_delete(context, vif_ref['id'])
+@require_context
+def virtual_interface_get_all(context):
+ """Get all vifs"""
+ session = get_session()
+ vif_refs = session.query(models.VirtualInterface).\
+ options(joinedload('network')).\
+ options(joinedload('fixed_ips')).\
+ all()
+ return vif_refs
+
+
###################
@@ -1172,7 +1177,6 @@ def _build_instance_get(context, session=None):
partial = session.query(models.Instance).\
options(joinedload_all('fixed_ips.floating_ips')).\
options(joinedload_all('fixed_ips.network')).\
- options(joinedload('virtual_interfaces')).\
options(joinedload_all('security_groups.rules')).\
options(joinedload('volumes')).\
options(joinedload('metadata')).\
@@ -1191,10 +1195,6 @@ def instance_get_all(context):
session = get_session()
return session.query(models.Instance).\
options(joinedload_all('fixed_ips.floating_ips')).\
- options(joinedload_all('virtual_interfaces.network')).\
- options(joinedload_all(
- 'virtual_interfaces.fixed_ips.floating_ips')).\
- options(joinedload('virtual_interfaces.instance')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
@@ -1209,27 +1209,6 @@ def instance_get_all_by_filters(context, filters):
will be returned by default, unless there's a filter that says
otherwise"""
- def _regexp_filter_by_ipv6(instance, filter_re):
- for interface in instance['virtual_interfaces']:
- fixed_ipv6 = interface.get('fixed_ipv6')
- if fixed_ipv6 and filter_re.match(fixed_ipv6):
- return True
- return False
-
- def _regexp_filter_by_ip(instance, filter_re):
- for interface in instance['virtual_interfaces']:
- for fixed_ip in interface['fixed_ips']:
- if not fixed_ip or not fixed_ip['address']:
- continue
- if filter_re.match(fixed_ip['address']):
- return True
- for floating_ip in fixed_ip.get('floating_ips', []):
- if not floating_ip or not floating_ip['address']:
- continue
- if filter_re.match(floating_ip['address']):
- return True
- return False
-
def _regexp_filter_by_metadata(instance, meta):
inst_metadata = [{node['key']: node['value']} \
for node in instance['metadata']]
@@ -1256,7 +1235,7 @@ def instance_get_all_by_filters(context, filters):
"""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:
@@ -1266,13 +1245,7 @@ def instance_get_all_by_filters(context, filters):
session = get_session()
query_prefix = session.query(models.Instance).\
- options(joinedload_all('fixed_ips.floating_ips')).\
- options(joinedload_all('virtual_interfaces.network')).\
- options(joinedload_all(
- 'virtual_interfaces.fixed_ips.floating_ips')).\
- options(joinedload('virtual_interfaces.instance')).\
options(joinedload('security_groups')).\
- options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
order_by(desc(models.Instance.created_at))
@@ -1296,7 +1269,7 @@ def instance_get_all_by_filters(context, filters):
# 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]
@@ -1308,15 +1281,13 @@ def instance_get_all_by_filters(context, filters):
filters.pop(filter_name))
instances = query_prefix.all()
-
if not instances:
return []
# 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..
- regexp_filter_funcs = {'ip6': _regexp_filter_by_ipv6,
- 'ip': _regexp_filter_by_ip}
+ regexp_filter_funcs = {}
for filter_name in filters.iterkeys():
filter_func = regexp_filter_funcs.get(filter_name, None)
@@ -1330,6 +1301,8 @@ def instance_get_all_by_filters(context, filters):
filter_l = lambda instance: _regexp_filter_by_column(instance,
filter_name, filter_re)
instances = filter(filter_l, instances)
+ if not instances:
+ break
return instances
@@ -1376,7 +1349,6 @@ def instance_get_all_by_user(context, user_id):
session = get_session()
return session.query(models.Instance).\
options(joinedload_all('fixed_ips.floating_ips')).\
- options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
@@ -1391,7 +1363,6 @@ def instance_get_all_by_host(context, host):
session = get_session()
return session.query(models.Instance).\
options(joinedload_all('fixed_ips.floating_ips')).\
- options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
@@ -1408,7 +1379,6 @@ def instance_get_all_by_project(context, project_id):
session = get_session()
return session.query(models.Instance).\
options(joinedload_all('fixed_ips.floating_ips')).\
- options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
@@ -1424,7 +1394,6 @@ def instance_get_all_by_reservation(context, reservation_id):
query = session.query(models.Instance).\
filter_by(reservation_id=reservation_id).\
options(joinedload_all('fixed_ips.floating_ips')).\
- options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
@@ -1441,36 +1410,11 @@ def instance_get_all_by_reservation(context, reservation_id):
all()
-@require_context
-def instance_get_by_fixed_ip(context, address):
- """Return instance ref by exact match of FixedIP"""
- fixed_ip_ref = fixed_ip_get_by_address(context, address)
- return fixed_ip_ref.instance
-
-
-@require_context
-def instance_get_by_fixed_ipv6(context, address):
- """Return instance ref by exact match of IPv6"""
- session = get_session()
-
- # convert IPv6 address to mac
- mac = ipv6.to_mac(address)
-
- # get virtual interface
- vif_ref = virtual_interface_get_by_address(context, mac)
-
- # look up instance based on instance_id from vif row
- result = session.query(models.Instance).\
- filter_by(id=vif_ref['instance_id'])
- return result
-
-
@require_admin_context
def instance_get_project_vpn(context, project_id):
session = get_session()
return session.query(models.Instance).\
options(joinedload_all('fixed_ips.floating_ips')).\
- options(joinedload('virtual_interfaces')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ips.network')).\
options(joinedload('metadata')).\
@@ -1602,6 +1546,18 @@ def instance_get_actions(context, instance_id):
all()
+@require_context
+def instance_get_id_to_uuid_mapping(context, ids):
+ session = get_session()
+ instances = session.query(models.Instance).\
+ filter(models.Instance.id.in_(ids)).\
+ all()
+ mapping = {}
+ for instance in instances:
+ mapping[instance['id']] = instance['uuid']
+ return mapping
+
+
###################
@@ -3199,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
new file mode 100644
index 000000000..ae9486e58
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py
@@ -0,0 +1,68 @@
+# 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.
+
+from sqlalchemy import Column, Integer, MetaData, Table
+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
+ meta.bind = migrate_engine
+ dialect = migrate_engine.url.get_dialect().name
+ if dialect.startswith('sqlite'):
+ return
+
+ try:
+ ForeignKeyConstraint(columns=[vifs.c.instance_id],
+ refcolumns=[instances.c.id]).drop()
+ except Exception:
+ try:
+ migrate_engine.execute("ALTER TABLE virtual_interfaces DROP " \
+ "FOREIGN KEY " \
+ "`virtual_interfaces_ibfk_2`;")
+ except Exception:
+ logging.error(_("foreign key constraint couldn't be removed"))
+ raise
+
+
+def downgrade(migrate_engine):
+ # Operations to reverse the above upgrade go here.
+ meta.bind = migrate_engine
+ dialect = migrate_engine.url.get_dialect().name
+ if dialect.startswith('sqlite'):
+ return
+
+ try:
+ ForeignKeyConstraint(columns=[vifs.c.instance_id],
+ refcolumns=[instances.c.id]).create()
+ except Exception:
+ logging.error(_("foreign key constraint couldn't be added"))
+ raise
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/047_sqlite_downgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/047_sqlite_downgrade.sql
new file mode 100644
index 000000000..cf9afbb09
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/047_sqlite_downgrade.sql
@@ -0,0 +1,47 @@
+COMMIT;
+BEGIN TRANSACTION;
+ CREATE TEMPORARY TABLE virtual_interfaces_backup (
+ created_at DATETIME,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN,
+ id INTEGER NOT NULL,
+ address VARCHAR(255),
+ network_id INTEGER,
+ instance_id INTEGER NOT NULL,
+ uuid VARCHAR(36),
+ PRIMARY KEY (id)
+ );
+
+ INSERT INTO virtual_interfaces_backup
+ SELECT created_at, updated_at, deleted_at, deleted, id, address,
+ network_id, instance_id, uuid
+ FROM virtual_interfaces;
+
+ DROP TABLE virtual_interfaces;
+
+ CREATE TABLE virtual_interfaces (
+ created_at DATETIME,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN,
+ id INTEGER NOT NULL,
+ address VARCHAR(255),
+ network_id INTEGER,
+ instance_id INTEGER NOT NULL,
+ uuid VARCHAR(36),
+ PRIMARY KEY (id),
+ FOREIGN KEY(network_id) REFERENCES networks (id),
+ FOREIGN KEY(instance_id) REFERENCES instances (id),
+ UNIQUE (address),
+ CHECK (deleted IN (0, 1))
+ );
+
+ INSERT INTO virtual_interfaces
+ SELECT created_at, updated_at, deleted_at, deleted, id, address,
+ network_id, instance_id, uuid
+ FROM virtual_interfaces_backup;
+
+ DROP TABLE virtual_interfaces_backup;
+
+COMMIT;
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/047_sqlite_upgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/047_sqlite_upgrade.sql
new file mode 100644
index 000000000..2c0919f1d
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/047_sqlite_upgrade.sql
@@ -0,0 +1,45 @@
+BEGIN TRANSACTION;
+ CREATE TEMPORARY TABLE virtual_interfaces_backup (
+ created_at DATETIME,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN,
+ id INTEGER NOT NULL,
+ address VARCHAR(255),
+ network_id INTEGER,
+ instance_id INTEGER NOT NULL,
+ uuid VARCHAR(36),
+ PRIMARY KEY (id)
+ );
+
+ INSERT INTO virtual_interfaces_backup
+ SELECT created_at, updated_at, deleted_at, deleted, id, address,
+ network_id, instance_id, uuid
+ FROM virtual_interfaces;
+
+ DROP TABLE virtual_interfaces;
+
+ CREATE TABLE virtual_interfaces (
+ created_at DATETIME,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN,
+ id INTEGER NOT NULL,
+ address VARCHAR(255),
+ network_id INTEGER,
+ instance_id INTEGER NOT NULL,
+ uuid VARCHAR(36),
+ PRIMARY KEY (id),
+ FOREIGN KEY(network_id) REFERENCES networks (id),
+ UNIQUE (address),
+ CHECK (deleted IN (0, 1))
+ );
+
+ INSERT INTO virtual_interfaces
+ SELECT created_at, updated_at, deleted_at, deleted, id, address,
+ network_id, instance_id, uuid
+ FROM virtual_interfaces_backup;
+
+ DROP TABLE virtual_interfaces_backup;
+
+COMMIT;
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index b5f30a1e3..089cd5450 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -642,10 +642,7 @@ class VirtualInterface(BASE, NovaBase):
address = Column(String(255), unique=True)
network_id = Column(Integer, ForeignKey('networks.id'))
network = relationship(Network, backref=backref('virtual_interfaces'))
-
- # TODO(tr3buchet): cut the cord, removed foreign key and backrefs
- instance_id = Column(Integer, ForeignKey('instances.id'), nullable=False)
- instance = relationship(Instance, backref=backref('virtual_interfaces'))
+ instance_id = Column(Integer, nullable=False)
uuid = Column(String(36))
diff --git a/nova/network/api.py b/nova/network/api.py
index 78580d360..a1ed28496 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -46,8 +46,9 @@ class API(base.Base):
return ips
def get_vifs_by_instance(self, context, instance_id):
- vifs = self.db.virtual_interface_get_by_instance(context, instance_id)
- return vifs
+ return rpc.call(context, FLAGS.network_topic,
+ {'method': 'get_vifs_by_instance',
+ 'args': {'instance_id': instance_id}})
def allocate_floating_ip(self, context):
"""Adds a floating ip to a project."""
@@ -210,3 +211,12 @@ class API(base.Base):
return rpc.call(context, FLAGS.network_topic,
{'method': 'validate_networks',
'args': args})
+
+ def get_instance_uuids_by_ip_filter(self, context, filters):
+ """Returns a list of dicts in the form of
+ {'instance_uuid': uuid, 'ip': ip} that matched the ip_filter
+ """
+ args = {'filters': filters}
+ return rpc.call(context, FLAGS.network_topic,
+ {'method': 'get_instance_uuids_by_ip_filter',
+ 'args': args})
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index e693e5939..9bf98fc27 100755
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -550,6 +550,10 @@ def get_dhcp_opts(context, network_ref):
return '\n'.join(hosts)
+def release_dhcp(dev, address, mac_address):
+ utils.execute('dhcp_release', dev, address, mac_address, run_as_root=True)
+
+
# NOTE(ja): Sending a HUP only reloads the hostfile, so any
# configuration options (like dchp-range, vlan, ...)
# aren't reloaded.
@@ -594,7 +598,6 @@ def update_dhcp(context, dev, network_ref):
'dnsmasq',
'--strict-order',
'--bind-interfaces',
- '--interface=%s' % dev,
'--conf-file=%s' % FLAGS.dnsmasq_config_file,
'--domain=%s' % FLAGS.dhcp_domain,
'--pid-file=%s' % _dhcp_file(dev, 'pid'),
@@ -800,6 +803,10 @@ def unplug(network):
return interface_driver.unplug(network)
+def get_dev(network):
+ return interface_driver.get_dev(network)
+
+
class LinuxNetInterfaceDriver(object):
"""Abstract class that defines generic network host API"""
""" for for all Linux interface drivers."""
@@ -812,6 +819,10 @@ class LinuxNetInterfaceDriver(object):
"""Destory Linux device, return device name"""
raise NotImplementedError()
+ def get_dev(self, network):
+ """Get device name"""
+ raise NotImplementedError()
+
# plugs interfaces using Linux Bridge
class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
@@ -833,6 +844,9 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
return network['bridge']
def unplug(self, network):
+ return self.get_dev(network)
+
+ def get_dev(self, network):
return network['bridge']
@classmethod
@@ -957,6 +971,9 @@ class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver):
return dev
def unplug(self, network):
+ return self.get_dev(network)
+
+ def get_dev(self, network):
dev = "gw-" + str(network['id'])
return dev
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 70e51888f..ffb9f976c 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -48,6 +48,7 @@ import datetime
import itertools
import math
import netaddr
+import re
import socket
from eventlet import greenpool
@@ -110,6 +111,8 @@ flags.DEFINE_string('network_host', socket.gethostname(),
'Network host to use for ip allocation in flat modes')
flags.DEFINE_bool('fake_call', False,
'If True, skip using the queue and make local calls')
+flags.DEFINE_bool('force_dhcp_release', False,
+ 'If True, send a dhcp release on instance termination')
class AddressAlreadyAllocated(exception.Error):
@@ -397,6 +400,59 @@ class NetworkManager(manager.SchedulerDependentManager):
self.compute_api.trigger_security_group_members_refresh(admin_context,
group_ids)
+ def get_vifs_by_instance(self, context, instance_id):
+ vifs = self.db.virtual_interface_get_by_instance(context,
+ instance_id)
+ return vifs
+
+ def get_instance_uuids_by_ip_filter(self, context, filters):
+ fixed_ip_filter = filters.get('fixed_ip')
+ ip_filter = re.compile(str(filters.get('ip')))
+ ipv6_filter = re.compile(str(filters.get('ip6')))
+
+ # NOTE(jkoelker) Should probably figure out a better way to do
+ # this. But for now it "works", this could suck on
+ # large installs.
+
+ vifs = self.db.virtual_interface_get_all(context)
+ results = []
+
+ for vif in vifs:
+ if vif['instance_id'] is None:
+ continue
+
+ fixed_ipv6 = vif.get('fixed_ipv6')
+ if fixed_ipv6 and ipv6_filter.match(fixed_ipv6):
+ # NOTE(jkoelker) Will need to update for the UUID flip
+ results.append({'instance_id': vif['instance_id'],
+ 'ip': fixed_ipv6})
+
+ for fixed_ip in vif['fixed_ips']:
+ if not fixed_ip or not fixed_ip['address']:
+ continue
+ if fixed_ip['address'] == fixed_ip_filter:
+ results.append({'instance_id': vif['instance_id'],
+ 'ip': fixed_ip['address']})
+ continue
+ if ip_filter.match(fixed_ip['address']):
+ results.append({'instance_id': vif['instance_id'],
+ 'ip': fixed_ip['address']})
+ continue
+ for floating_ip in fixed_ip.get('floating_ips', []):
+ if not floating_ip or not floating_ip['address']:
+ continue
+ if ip_filter.match(floating_ip['address']):
+ results.append({'instance_id': vif['instance_id'],
+ 'ip': floating_ip['address']})
+ continue
+
+ # NOTE(jkoelker) Until we switch over to instance_uuid ;)
+ ids = [res['instance_id'] for res in results]
+ uuid_map = self.db.instance_get_id_to_uuid_mapping(context, ids)
+ for res in results:
+ res['instance_uuid'] = uuid_map.get(res['instance_id'])
+ return results
+
def _get_networks_for_instance(self, context, instance_id, project_id,
requested_networks=None):
"""Determine & return which networks an instance should connect to."""
@@ -629,6 +685,11 @@ class NetworkManager(manager.SchedulerDependentManager):
instance_id = instance_ref['id']
self._do_trigger_security_group_members_refresh_for_instance(
instance_id)
+ if FLAGS.force_dhcp_release:
+ dev = self.driver.get_dev(fixed_ip_ref['network'])
+ vif = self.db.virtual_interface_get_by_instance_and_network(
+ context, instance_ref['id'], fixed_ip_ref['network']['id'])
+ self.driver.release_dhcp(dev, address, vif['address'])
def lease_fixed_ip(self, context, address):
"""Called by dhcp-bridge when ip is leased."""
diff --git a/nova/tests/api/openstack/contrib/test_virtual_interfaces.py b/nova/tests/api/openstack/contrib/test_virtual_interfaces.py
index 1db253b35..0260e89d4 100644
--- a/nova/tests/api/openstack/contrib/test_virtual_interfaces.py
+++ b/nova/tests/api/openstack/contrib/test_virtual_interfaces.py
@@ -14,22 +14,20 @@
# under the License.
import json
-import stubout
import webob
from nova import test
-from nova import compute
+from nova import network
from nova.tests.api.openstack import fakes
from nova.api.openstack.contrib.virtual_interfaces import \
ServerVirtualInterfaceController
-def compute_api_get(self, context, server_id):
- return {'virtual_interfaces': [
- {'uuid': '00000000-0000-0000-0000-00000000000000000',
- 'address': '00-00-00-00-00-00'},
- {'uuid': '11111111-1111-1111-1111-11111111111111111',
- 'address': '11-11-11-11-11-11'}]}
+def get_vifs_by_instance(self, context, server_id):
+ return [{'uuid': '00000000-0000-0000-0000-00000000000000000',
+ 'address': '00-00-00-00-00-00'},
+ {'uuid': '11111111-1111-1111-1111-11111111111111111',
+ 'address': '11-11-11-11-11-11'}]
class ServerVirtualInterfaceTest(test.TestCase):
@@ -37,7 +35,8 @@ class ServerVirtualInterfaceTest(test.TestCase):
def setUp(self):
super(ServerVirtualInterfaceTest, self).setUp()
self.controller = ServerVirtualInterfaceController()
- self.stubs.Set(compute.api.API, "get", compute_api_get)
+ self.stubs.Set(network.api.API, "get_vifs_by_instance",
+ get_vifs_by_instance)
def tearDown(self):
super(ServerVirtualInterfaceTest, self).tearDown()
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index b3e4ad0cf..cccbafcde 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -1309,7 +1309,7 @@ class ServersTest(test.TestCase):
self.assertEqual(servers[0]['id'], 100)
def test_tenant_id_filter_converts_to_project_id_for_admin(self):
- def fake_get_all(context, filters=None):
+ def fake_get_all(context, filters=None, instances=None):
self.assertNotEqual(filters, None)
self.assertEqual(filters['project_id'], 'faketenant')
self.assertFalse(filters.get('tenant_id'))
@@ -2255,6 +2255,58 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 400)
+ def test_create_instance_v1_1_malformed_entity(self):
+ self._setup_for_create_instance()
+ req = webob.Request.blank('/v1.1/fake/servers')
+ req.method = 'POST'
+ req.body = json.dumps({'server': 'string'})
+ req.headers['content-type'] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+
+ def test_create_instance_v1_1_malformed_body_string(self):
+ self._setup_for_create_instance()
+ req = webob.Request.blank('/v1.1/fake/servers')
+ req.method = 'POST'
+ req.body = 'string'
+ req.headers['content-type'] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+
+ def test_create_instance_v1_1_malformed_body_list(self):
+ self._setup_for_create_instance()
+ body = ['string']
+ req = webob.Request.blank('/v1.1/fake/servers')
+ req.method = 'POST'
+ req.body = json.dumps(['string'])
+ req.headers['content-type'] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 422)
+
+ def test_create_instance_v1_0_malformed_entity(self):
+ req = webob.Request.blank('/v1.0/servers')
+ req.method = 'POST'
+ req.body = json.dumps({'server': 'string'})
+ req.headers['content-type'] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+
+ def test_create_instance_v1_0_malformed_body_string(self):
+ req = webob.Request.blank('/v1.0/servers')
+ req.method = 'POST'
+ req.body = 'string'
+ req.headers['content-type'] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+
+ def test_create_instance_v1_0_malformed_body_list(self):
+ req = webob.Request.blank('/v1.0/servers')
+ req.method = 'POST'
+ req.body = json.dumps(['string'])
+ req.headers['content-type'] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 422)
+
def test_update_server_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py
index 142206755..febac5e09 100644
--- a/nova/tests/fake_network.py
+++ b/nova/tests/fake_network.py
@@ -16,8 +16,9 @@
# under the License.
from nova import db
+from nova import exception
from nova import flags
-from nova import test
+from nova import utils
from nova.network import manager as network_manager
@@ -64,6 +65,64 @@ class FakeModel(dict):
return self[name]
+class FakeNetworkManager(network_manager.NetworkManager):
+ """This NetworkManager doesn't call the base class so we can bypass all
+ inherited service cruft and just perform unit tests.
+ """
+
+ class FakeDB:
+ def fixed_ip_get_by_instance(self, context, instance_id):
+ return [dict(address='10.0.0.0'), dict(address='10.0.0.1'),
+ dict(address='10.0.0.2')]
+
+ def network_get_by_cidr(self, context, cidr):
+ raise exception.NetworkNotFoundForCidr()
+
+ def network_create_safe(self, context, net):
+ fakenet = dict(net)
+ fakenet['id'] = 999
+ return fakenet
+
+ def network_get_all(self, context):
+ raise exception.NoNetworksFound()
+
+ def virtual_interface_get_all(self, context):
+ floats = [{'address': '172.16.1.1'},
+ {'address': '172.16.1.2'},
+ {'address': '173.16.1.2'}]
+
+ vifs = [{'instance_id': 0,
+ 'fixed_ipv6': '2001:db8::dcad:beff:feef:1',
+ 'fixed_ips': [{'address': '172.16.0.1',
+ 'floating_ips': [floats[0]]}]},
+ {'instance_id': 20,
+ 'fixed_ipv6': '2001:db8::dcad:beff:feef:2',
+ 'fixed_ips': [{'address': '172.16.0.2',
+ 'floating_ips': [floats[1]]}]},
+ {'instance_id': 30,
+ 'fixed_ipv6': '2002:db8::dcad:beff:feef:2',
+ 'fixed_ips': [{'address': '173.16.0.2',
+ 'floating_ips': [floats[2]]}]}]
+ return vifs
+
+ def instance_get_id_to_uuid_mapping(self, context, ids):
+ # NOTE(jkoelker): This is just here until we can rely on UUIDs
+ mapping = {}
+ for id in ids:
+ mapping[id] = str(utils.gen_uuid())
+ return mapping
+
+ def __init__(self):
+ self.db = self.FakeDB()
+ self.deallocate_called = None
+
+ def deallocate_fixed_ip(self, context, address):
+ self.deallocate_called = address
+
+ def _create_fixed_ips(self, context, network_id):
+ pass
+
+
flavor = {'id': 0,
'name': 'fake_flavor',
'memory_mb': 2048,
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 356412dbf..948c7ad40 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -21,22 +21,24 @@ Tests For Compute
"""
from nova import compute
-from nova.compute import instance_types
-from nova.compute import manager as compute_manager
-from nova.compute import power_state
-from nova.compute import vm_states
from nova import context
from nova import db
-from nova.db.sqlalchemy import models
-from nova.db.sqlalchemy import api as sqlalchemy_api
from nova import exception
from nova import flags
-import nova.image.fake
from nova import log as logging
from nova import rpc
from nova import test
from nova import utils
+
+from nova.compute import instance_types
+from nova.compute import manager as compute_manager
+from nova.compute import power_state
+from nova.compute import vm_states
+from nova.db.sqlalchemy import models
+from nova.image import fake as fake_image
from nova.notifier import test_notifier
+from nova.tests import fake_network
+
LOG = logging.getLogger('nova.tests.compute')
FLAGS = flags.FLAGS
@@ -74,7 +76,7 @@ class ComputeTestCase(test.TestCase):
def fake_show(meh, context, id):
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
- self.stubs.Set(nova.image.fake._FakeImageService, 'show', fake_show)
+ self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
def _create_instance(self, params=None):
"""Create a test instance"""
@@ -1023,190 +1025,19 @@ class ComputeTestCase(test.TestCase):
db.instance_destroy(c, instance_id2)
db.instance_destroy(c, instance_id3)
- def test_get_by_fixed_ip(self):
- """Test getting 1 instance by Fixed IP"""
- c = context.get_admin_context()
- instance_id1 = self._create_instance()
- instance_id2 = self._create_instance({'id': 20})
- instance_id3 = self._create_instance({'id': 30})
-
- vif_ref1 = db.virtual_interface_create(c,
- {'address': '12:34:56:78:90:12',
- 'instance_id': instance_id1,
- 'network_id': 1})
- vif_ref2 = db.virtual_interface_create(c,
- {'address': '90:12:34:56:78:90',
- 'instance_id': instance_id2,
- 'network_id': 1})
-
- db.fixed_ip_create(c,
- {'address': '1.1.1.1',
- 'instance_id': instance_id1,
- 'virtual_interface_id': vif_ref1['id']})
- db.fixed_ip_create(c,
- {'address': '1.1.2.1',
- 'instance_id': instance_id2,
- 'virtual_interface_id': vif_ref2['id']})
-
- # regex not allowed
- instances = self.compute_api.get_all(c,
- search_opts={'fixed_ip': '.*'})
- self.assertEqual(len(instances), 0)
-
- instances = self.compute_api.get_all(c,
- search_opts={'fixed_ip': '1.1.3.1'})
- self.assertEqual(len(instances), 0)
-
- instances = self.compute_api.get_all(c,
- search_opts={'fixed_ip': '1.1.1.1'})
- self.assertEqual(len(instances), 1)
- self.assertEqual(instances[0].id, instance_id1)
-
- instances = self.compute_api.get_all(c,
- search_opts={'fixed_ip': '1.1.2.1'})
- self.assertEqual(len(instances), 1)
- self.assertEqual(instances[0].id, instance_id2)
-
- db.virtual_interface_delete(c, vif_ref1['id'])
- db.virtual_interface_delete(c, vif_ref2['id'])
- db.instance_destroy(c, instance_id1)
- db.instance_destroy(c, instance_id2)
-
- def test_get_all_by_ip_regexp(self):
- """Test searching by Floating and Fixed IP"""
- c = context.get_admin_context()
- instance_id1 = self._create_instance({'display_name': 'woot'})
- instance_id2 = self._create_instance({
- 'display_name': 'woo',
- 'id': 20})
- instance_id3 = self._create_instance({
- 'display_name': 'not-woot',
- 'id': 30})
-
- vif_ref1 = db.virtual_interface_create(c,
- {'address': '12:34:56:78:90:12',
- 'instance_id': instance_id1,
- 'network_id': 1})
- vif_ref2 = db.virtual_interface_create(c,
- {'address': '90:12:34:56:78:90',
- 'instance_id': instance_id2,
- 'network_id': 1})
- vif_ref3 = db.virtual_interface_create(c,
- {'address': '34:56:78:90:12:34',
- 'instance_id': instance_id3,
- 'network_id': 1})
-
- db.fixed_ip_create(c,
- {'address': '1.1.1.1',
- 'instance_id': instance_id1,
- 'virtual_interface_id': vif_ref1['id']})
- db.fixed_ip_create(c,
- {'address': '1.1.2.1',
- 'instance_id': instance_id2,
- 'virtual_interface_id': vif_ref2['id']})
- fix_addr = db.fixed_ip_create(c,
- {'address': '1.1.3.1',
- 'instance_id': instance_id3,
- 'virtual_interface_id': vif_ref3['id']})
- fix_ref = db.fixed_ip_get_by_address(c, fix_addr)
- flo_ref = db.floating_ip_create(c,
- {'address': '10.0.0.2',
- 'fixed_ip_id': fix_ref['id']})
-
- # ends up matching 2nd octet here.. so all 3 match
- instances = self.compute_api.get_all(c,
- search_opts={'ip': '.*\.1'})
- self.assertEqual(len(instances), 3)
-
- instances = self.compute_api.get_all(c,
- search_opts={'ip': '1.*'})
- self.assertEqual(len(instances), 3)
-
- instances = self.compute_api.get_all(c,
- search_opts={'ip': '.*\.1.\d+$'})
- self.assertEqual(len(instances), 1)
- instance_ids = [instance.id for instance in instances]
- self.assertTrue(instance_id1 in instance_ids)
-
- instances = self.compute_api.get_all(c,
- search_opts={'ip': '.*\.2.+'})
- self.assertEqual(len(instances), 1)
- self.assertEqual(instances[0].id, instance_id2)
-
- instances = self.compute_api.get_all(c,
- search_opts={'ip': '10.*'})
- self.assertEqual(len(instances), 1)
- self.assertEqual(instances[0].id, instance_id3)
-
- db.virtual_interface_delete(c, vif_ref1['id'])
- db.virtual_interface_delete(c, vif_ref2['id'])
- db.virtual_interface_delete(c, vif_ref3['id'])
- db.floating_ip_destroy(c, '10.0.0.2')
- db.instance_destroy(c, instance_id1)
- db.instance_destroy(c, instance_id2)
- db.instance_destroy(c, instance_id3)
-
- def test_get_all_by_ipv6_regexp(self):
- """Test searching by IPv6 address"""
-
- c = context.get_admin_context()
- instance_id1 = self._create_instance({'display_name': 'woot'})
- instance_id2 = self._create_instance({
- 'display_name': 'woo',
- 'id': 20})
- instance_id3 = self._create_instance({
- 'display_name': 'not-woot',
- 'id': 30})
-
- vif_ref1 = db.virtual_interface_create(c,
- {'address': '12:34:56:78:90:12',
- 'instance_id': instance_id1,
- 'network_id': 1})
- vif_ref2 = db.virtual_interface_create(c,
- {'address': '90:12:34:56:78:90',
- 'instance_id': instance_id2,
- 'network_id': 1})
- vif_ref3 = db.virtual_interface_create(c,
- {'address': '34:56:78:90:12:34',
- 'instance_id': instance_id3,
- 'network_id': 1})
-
- # This will create IPv6 addresses of:
- # 1: fd00::1034:56ff:fe78:9012
- # 20: fd00::9212:34ff:fe56:7890
- # 30: fd00::3656:78ff:fe90:1234
-
- instances = self.compute_api.get_all(c,
- search_opts={'ip6': '.*1034.*'})
- self.assertEqual(len(instances), 1)
- self.assertEqual(instances[0].id, instance_id1)
-
- instances = self.compute_api.get_all(c,
- search_opts={'ip6': '^fd00.*'})
- self.assertEqual(len(instances), 3)
- instance_ids = [instance.id for instance in instances]
- self.assertTrue(instance_id1 in instance_ids)
- self.assertTrue(instance_id2 in instance_ids)
- self.assertTrue(instance_id3 in instance_ids)
-
- instances = self.compute_api.get_all(c,
- search_opts={'ip6': '^.*12.*34.*'})
- self.assertEqual(len(instances), 2)
- instance_ids = [instance.id for instance in instances]
- self.assertTrue(instance_id2 in instance_ids)
- self.assertTrue(instance_id3 in instance_ids)
-
- db.virtual_interface_delete(c, vif_ref1['id'])
- db.virtual_interface_delete(c, vif_ref2['id'])
- db.virtual_interface_delete(c, vif_ref3['id'])
- db.instance_destroy(c, instance_id1)
- db.instance_destroy(c, instance_id2)
- db.instance_destroy(c, instance_id3)
-
def test_get_all_by_multiple_options_at_once(self):
"""Test searching by multiple options at once"""
c = context.get_admin_context()
- instance_id1 = self._create_instance({'display_name': 'woot'})
+ network_manager = fake_network.FakeNetworkManager()
+ self.stubs.Set(self.compute_api.network_api,
+ 'get_instance_uuids_by_ip_filter',
+ network_manager.get_instance_uuids_by_ip_filter)
+ self.stubs.Set(network_manager.db,
+ 'instance_get_id_to_uuid_mapping',
+ db.instance_get_id_to_uuid_mapping)
+
+ instance_id1 = self._create_instance({'display_name': 'woot',
+ 'id': 0})
instance_id2 = self._create_instance({
'display_name': 'woo',
'id': 20})
@@ -1214,36 +1045,6 @@ class ComputeTestCase(test.TestCase):
'display_name': 'not-woot',
'id': 30})
- vif_ref1 = db.virtual_interface_create(c,
- {'address': '12:34:56:78:90:12',
- 'instance_id': instance_id1,
- 'network_id': 1})
- vif_ref2 = db.virtual_interface_create(c,
- {'address': '90:12:34:56:78:90',
- 'instance_id': instance_id2,
- 'network_id': 1})
- vif_ref3 = db.virtual_interface_create(c,
- {'address': '34:56:78:90:12:34',
- 'instance_id': instance_id3,
- 'network_id': 1})
-
- db.fixed_ip_create(c,
- {'address': '1.1.1.1',
- 'instance_id': instance_id1,
- 'virtual_interface_id': vif_ref1['id']})
- db.fixed_ip_create(c,
- {'address': '1.1.2.1',
- 'instance_id': instance_id2,
- 'virtual_interface_id': vif_ref2['id']})
- fix_addr = db.fixed_ip_create(c,
- {'address': '1.1.3.1',
- 'instance_id': instance_id3,
- 'virtual_interface_id': vif_ref3['id']})
- fix_ref = db.fixed_ip_get_by_address(c, fix_addr)
- flo_ref = db.floating_ip_create(c,
- {'address': '10.0.0.2',
- 'fixed_ip_id': fix_ref['id']})
-
# ip ends up matching 2nd octet here.. so all 3 match ip
# but 'name' only matches one
instances = self.compute_api.get_all(c,
@@ -1251,18 +1052,18 @@ class ComputeTestCase(test.TestCase):
self.assertEqual(len(instances), 1)
self.assertEqual(instances[0].id, instance_id3)
- # ip ends up matching any ip with a '2' in it.. so instance
- # 2 and 3.. but name should only match #2
+ # ip ends up matching any ip with a '1' in the last octet..
+ # so instance 1 and 3.. but name should only match #1
# but 'name' only matches one
instances = self.compute_api.get_all(c,
- search_opts={'ip': '.*2', 'name': '^woo.*'})
+ search_opts={'ip': '.*\.1$', 'name': '^woo.*'})
self.assertEqual(len(instances), 1)
- self.assertEqual(instances[0].id, instance_id2)
+ self.assertEqual(instances[0].id, instance_id1)
# same as above but no match on name (name matches instance_id1
# but the ip query doesn't
instances = self.compute_api.get_all(c,
- search_opts={'ip': '.*2.*', 'name': '^woot.*'})
+ search_opts={'ip': '.*\.2$', 'name': '^woot.*'})
self.assertEqual(len(instances), 0)
# ip matches all 3... ipv6 matches #2+#3...name matches #3
@@ -1273,10 +1074,6 @@ class ComputeTestCase(test.TestCase):
self.assertEqual(len(instances), 1)
self.assertEqual(instances[0].id, instance_id3)
- db.virtual_interface_delete(c, vif_ref1['id'])
- db.virtual_interface_delete(c, vif_ref2['id'])
- db.virtual_interface_delete(c, vif_ref3['id'])
- db.floating_ip_destroy(c, '10.0.0.2')
db.instance_destroy(c, instance_id1)
db.instance_destroy(c, instance_id2)
db.instance_destroy(c, instance_id3)
diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py
index 60d7abd8c..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, inst2.id)
- self.assertEqual(result[1].id, inst1.id)
- self.assertTrue(result[1].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_metadata.py b/nova/tests/test_metadata.py
index b06e5c136..2f82132fa 100644
--- a/nova/tests/test_metadata.py
+++ b/nova/tests/test_metadata.py
@@ -19,22 +19,21 @@
"""Tests for the testing the metadata code."""
import base64
-import httplib
import webob
from nova import exception
from nova import test
-from nova import wsgi
from nova.api.ec2 import metadatarequesthandler
from nova.db.sqlalchemy import api
+from nova.tests import fake_network
USER_DATA_STRING = ("This is an encoded string")
ENCODE_USER_DATA_STRING = base64.b64encode(USER_DATA_STRING)
-def return_non_existing_server_by_address(context, address):
+def return_non_existing_server_by_address(context, address, *args, **kwarg):
raise exception.NotFound()
@@ -69,6 +68,10 @@ class MetadataTestCase(test.TestCase):
self.stubs.Set(api, 'instance_get_all_by_filters', instance_get_list)
self.stubs.Set(api, 'instance_get_floating_address', floating_get)
self.app = metadatarequesthandler.MetadataRequestHandler()
+ network_manager = fake_network.FakeNetworkManager()
+ self.stubs.Set(self.app.cc.network_api,
+ 'get_instance_uuids_by_ip_filter',
+ network_manager.get_instance_uuids_by_ip_filter)
def request(self, relative_url):
request = webob.Request.blank(relative_url)
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index 926ea065a..15c179177 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -438,55 +438,23 @@ class VlanNetworkTestCase(test.TestCase):
class CommonNetworkTestCase(test.TestCase):
-
- class FakeNetworkManager(network_manager.NetworkManager):
- """This NetworkManager doesn't call the base class so we can bypass all
- inherited service cruft and just perform unit tests.
- """
-
- class FakeDB:
- def fixed_ip_get_by_instance(self, context, instance_id):
- return [dict(address='10.0.0.0'), dict(address='10.0.0.1'),
- dict(address='10.0.0.2')]
-
- def network_get_by_cidr(self, context, cidr):
- raise exception.NetworkNotFoundForCidr()
-
- def network_create_safe(self, context, net):
- fakenet = dict(net)
- fakenet['id'] = 999
- return fakenet
-
- def network_get_all(self, context):
- raise exception.NoNetworksFound()
-
- def __init__(self):
- self.db = self.FakeDB()
- self.deallocate_called = None
-
- def deallocate_fixed_ip(self, context, address):
- self.deallocate_called = address
-
- def _create_fixed_ips(self, context, network_id):
- pass
-
def fake_create_fixed_ips(self, context, network_id):
return None
def test_remove_fixed_ip_from_instance(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
manager.remove_fixed_ip_from_instance(None, 99, '10.0.0.1')
self.assertEquals(manager.deallocate_called, '10.0.0.1')
def test_remove_fixed_ip_from_instance_bad_input(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.assertRaises(exception.FixedIpNotFoundForSpecificInstance,
manager.remove_fixed_ip_from_instance,
None, 99, 'bad input')
def test_validate_cidrs(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
nets = manager.create_networks(None, 'fake', '192.168.0.0/24',
False, 1, 256, None, None, None,
None)
@@ -495,7 +463,7 @@ class CommonNetworkTestCase(test.TestCase):
self.assertTrue('192.168.0.0/24' in cidrs)
def test_validate_cidrs_split_exact_in_half(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
nets = manager.create_networks(None, 'fake', '192.168.0.0/24',
False, 2, 128, None, None, None,
None)
@@ -505,7 +473,7 @@ class CommonNetworkTestCase(test.TestCase):
self.assertTrue('192.168.0.128/25' in cidrs)
def test_validate_cidrs_split_cidr_in_use_middle_of_range(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.mox.StubOutWithMock(manager.db, 'network_get_all')
ctxt = mox.IgnoreArg()
manager.db.network_get_all(ctxt).AndReturn([{'id': 1,
@@ -523,7 +491,7 @@ class CommonNetworkTestCase(test.TestCase):
self.assertFalse('192.168.2.0/24' in cidrs)
def test_validate_cidrs_smaller_subnet_in_use(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.mox.StubOutWithMock(manager.db, 'network_get_all')
ctxt = mox.IgnoreArg()
manager.db.network_get_all(ctxt).AndReturn([{'id': 1,
@@ -536,7 +504,7 @@ class CommonNetworkTestCase(test.TestCase):
self.assertRaises(ValueError, manager.create_networks, *args)
def test_validate_cidrs_split_smaller_cidr_in_use(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.mox.StubOutWithMock(manager.db, 'network_get_all')
ctxt = mox.IgnoreArg()
manager.db.network_get_all(ctxt).AndReturn([{'id': 1,
@@ -553,7 +521,7 @@ class CommonNetworkTestCase(test.TestCase):
self.assertFalse('192.168.2.0/24' in cidrs)
def test_validate_cidrs_split_smaller_cidr_in_use2(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.mox.StubOutWithMock(manager.db, 'network_get_all')
ctxt = mox.IgnoreArg()
manager.db.network_get_all(ctxt).AndReturn([{'id': 1,
@@ -569,7 +537,7 @@ class CommonNetworkTestCase(test.TestCase):
self.assertFalse('192.168.2.0/27' in cidrs)
def test_validate_cidrs_split_all_in_use(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.mox.StubOutWithMock(manager.db, 'network_get_all')
ctxt = mox.IgnoreArg()
in_use = [{'id': 1, 'cidr': '192.168.2.9/29'},
@@ -585,14 +553,14 @@ class CommonNetworkTestCase(test.TestCase):
self.assertRaises(ValueError, manager.create_networks, *args)
def test_validate_cidrs_one_in_use(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
args = (None, 'fake', '192.168.0.0/24', False, 2, 256, None, None,
None, None)
# ValueError: network_size * num_networks exceeds cidr size
self.assertRaises(ValueError, manager.create_networks, *args)
def test_validate_cidrs_already_used(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.mox.StubOutWithMock(manager.db, 'network_get_all')
ctxt = mox.IgnoreArg()
manager.db.network_get_all(ctxt).AndReturn([{'id': 1,
@@ -604,7 +572,7 @@ class CommonNetworkTestCase(test.TestCase):
self.assertRaises(ValueError, manager.create_networks, *args)
def test_validate_cidrs_too_many(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
args = (None, 'fake', '192.168.0.0/24', False, 200, 256, None, None,
None, None)
# ValueError: Not enough subnets avail to satisfy requested
@@ -612,7 +580,7 @@ class CommonNetworkTestCase(test.TestCase):
self.assertRaises(ValueError, manager.create_networks, *args)
def test_validate_cidrs_split_partial(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
nets = manager.create_networks(None, 'fake', '192.168.0.0/16',
False, 2, 256, None, None, None, None)
returned_cidrs = [str(net['cidr']) for net in nets]
@@ -620,7 +588,7 @@ class CommonNetworkTestCase(test.TestCase):
self.assertTrue('192.168.1.0/24' in returned_cidrs)
def test_validate_cidrs_conflict_existing_supernet(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.mox.StubOutWithMock(manager.db, 'network_get_all')
ctxt = mox.IgnoreArg()
fakecidr = [{'id': 1, 'cidr': '192.168.0.0/8'}]
@@ -634,16 +602,15 @@ class CommonNetworkTestCase(test.TestCase):
def test_create_networks(self):
cidr = '192.168.0.0/24'
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.stubs.Set(manager, '_create_fixed_ips',
self.fake_create_fixed_ips)
args = [None, 'foo', cidr, None, 1, 256, 'fd00::/48', None, None,
None]
- result = manager.create_networks(*args)
self.assertTrue(manager.create_networks(*args))
def test_create_networks_cidr_already_used(self):
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.mox.StubOutWithMock(manager.db, 'network_get_all')
ctxt = mox.IgnoreArg()
fakecidr = [{'id': 1, 'cidr': '192.168.0.0/24'}]
@@ -655,9 +622,124 @@ class CommonNetworkTestCase(test.TestCase):
def test_create_networks_many(self):
cidr = '192.168.0.0/16'
- manager = self.FakeNetworkManager()
+ manager = fake_network.FakeNetworkManager()
self.stubs.Set(manager, '_create_fixed_ips',
self.fake_create_fixed_ips)
args = [None, 'foo', cidr, None, 10, 256, 'fd00::/48', None, None,
None]
self.assertTrue(manager.create_networks(*args))
+
+ def test_get_instance_uuids_by_ip_regex(self):
+ manager = fake_network.FakeNetworkManager()
+ _vifs = manager.db.virtual_interface_get_all(None)
+
+ # Greedy get eveything
+ res = manager.get_instance_uuids_by_ip_filter(None, {'ip': '.*'})
+ self.assertEqual(len(res), len(_vifs))
+
+ # Doesn't exist
+ res = manager.get_instance_uuids_by_ip_filter(None, {'ip': '10.0.0.1'})
+ self.assertFalse(res)
+
+ # Get instance 1
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'ip': '172.16.0.2'})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 1)
+ self.assertEqual(res[0]['instance_id'], _vifs[1]['instance_id'])
+
+ # Get instance 2
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'ip': '173.16.0.2'})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 1)
+ self.assertEqual(res[0]['instance_id'], _vifs[2]['instance_id'])
+
+ # Get instance 0 and 1
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'ip': '172.16.0.*'})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 2)
+ self.assertEqual(res[0]['instance_id'], _vifs[0]['instance_id'])
+ self.assertEqual(res[1]['instance_id'], _vifs[1]['instance_id'])
+
+ # Get instance 1 and 2
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'ip': '17..16.0.2'})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 2)
+ self.assertEqual(res[0]['instance_id'], _vifs[1]['instance_id'])
+ self.assertEqual(res[1]['instance_id'], _vifs[2]['instance_id'])
+
+ def test_get_instance_uuids_by_ipv6_regex(self):
+ manager = fake_network.FakeNetworkManager()
+ _vifs = manager.db.virtual_interface_get_all(None)
+
+ # Greedy get eveything
+ res = manager.get_instance_uuids_by_ip_filter(None, {'ip6': '.*'})
+ self.assertEqual(len(res), len(_vifs))
+
+ # Doesn't exist
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'ip6': '.*1034.*'})
+ self.assertFalse(res)
+
+ # Get instance 1
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'ip6': '2001:.*:2'})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 1)
+ self.assertEqual(res[0]['instance_id'], _vifs[1]['instance_id'])
+
+ # Get instance 2
+ ip6 = '2002:db8::dcad:beff:feef:2'
+ res = manager.get_instance_uuids_by_ip_filter(None, {'ip6': ip6})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 1)
+ self.assertEqual(res[0]['instance_id'], _vifs[2]['instance_id'])
+
+ # Get instance 0 and 1
+ res = manager.get_instance_uuids_by_ip_filter(None, {'ip6': '2001:.*'})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 2)
+ self.assertEqual(res[0]['instance_id'], _vifs[0]['instance_id'])
+ self.assertEqual(res[1]['instance_id'], _vifs[1]['instance_id'])
+
+ # Get instance 1 and 2
+ ip6 = '200.:db8::dcad:beff:feef:2'
+ res = manager.get_instance_uuids_by_ip_filter(None, {'ip6': ip6})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 2)
+ self.assertEqual(res[0]['instance_id'], _vifs[1]['instance_id'])
+ self.assertEqual(res[1]['instance_id'], _vifs[2]['instance_id'])
+
+ def test_get_instance_uuids_by_ip(self):
+ manager = fake_network.FakeNetworkManager()
+ _vifs = manager.db.virtual_interface_get_all(None)
+
+ # No regex for you!
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'fixed_ip': '.*'})
+ self.assertFalse(res)
+
+ # Doesn't exist
+ ip = '10.0.0.1'
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'fixed_ip': ip})
+ self.assertFalse(res)
+
+ # Get instance 1
+ ip = '172.16.0.2'
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'fixed_ip': ip})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 1)
+ self.assertEqual(res[0]['instance_id'], _vifs[1]['instance_id'])
+
+ # Get instance 2
+ ip = '173.16.0.2'
+ res = manager.get_instance_uuids_by_ip_filter(None,
+ {'fixed_ip': ip})
+ self.assertTrue(res)
+ self.assertEqual(len(res), 1)
+ self.assertEqual(res[0]['instance_id'], _vifs[2]['instance_id'])
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 4a0849127..b3c8ebf6e 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)