revert live_migration branch
This commit is contained in:
parent
3fa93adb20
commit
d91229f7a3
2
.mailmap
2
.mailmap
@ -16,8 +16,6 @@
|
|||||||
<jmckenty@gmail.com> <jmckenty@joshua-mckentys-macbook-pro.local>
|
<jmckenty@gmail.com> <jmckenty@joshua-mckentys-macbook-pro.local>
|
||||||
<jmckenty@gmail.com> <joshua.mckenty@nasa.gov>
|
<jmckenty@gmail.com> <joshua.mckenty@nasa.gov>
|
||||||
<justin@fathomdb.com> <justinsb@justinsb-desktop>
|
<justin@fathomdb.com> <justinsb@justinsb-desktop>
|
||||||
<masumotok@nttdata.co.jp> <root@openstack2-api>
|
|
||||||
<masumotok@nttdata.co.jp> Masumoto<masumotok@nttdata.co.jp>
|
|
||||||
<mordred@inaugust.com> <mordred@hudson>
|
<mordred@inaugust.com> <mordred@hudson>
|
||||||
<paul@openstack.org> <pvoccio@castor.local>
|
<paul@openstack.org> <pvoccio@castor.local>
|
||||||
<paul@openstack.org> <paul.voccio@rackspace.com>
|
<paul@openstack.org> <paul.voccio@rackspace.com>
|
||||||
|
2
Authors
2
Authors
@ -26,7 +26,6 @@ Josh Durgin <joshd@hq.newdream.net>
|
|||||||
Josh Kearney <josh.kearney@rackspace.com>
|
Josh Kearney <josh.kearney@rackspace.com>
|
||||||
Joshua McKenty <jmckenty@gmail.com>
|
Joshua McKenty <jmckenty@gmail.com>
|
||||||
Justin Santa Barbara <justin@fathomdb.com>
|
Justin Santa Barbara <justin@fathomdb.com>
|
||||||
Kei Masumoto <masumotok@nttdata.co.jp>
|
|
||||||
Ken Pepple <ken.pepple@gmail.com>
|
Ken Pepple <ken.pepple@gmail.com>
|
||||||
Koji Iida <iida.koji@lab.ntt.co.jp>
|
Koji Iida <iida.koji@lab.ntt.co.jp>
|
||||||
Lorin Hochstein <lorin@isi.edu>
|
Lorin Hochstein <lorin@isi.edu>
|
||||||
@ -35,7 +34,6 @@ Michael Gundlach <michael.gundlach@rackspace.com>
|
|||||||
Monsyne Dragon <mdragon@rackspace.com>
|
Monsyne Dragon <mdragon@rackspace.com>
|
||||||
Monty Taylor <mordred@inaugust.com>
|
Monty Taylor <mordred@inaugust.com>
|
||||||
MORITA Kazutaka <morita.kazutaka@gmail.com>
|
MORITA Kazutaka <morita.kazutaka@gmail.com>
|
||||||
Muneyuki Noguchi <noguchimn@nttdata.co.jp>
|
|
||||||
Nachi Ueno <ueno.nachi@lab.ntt.co.jp> <openstack@lab.ntt.co.jp> <nati.ueno@gmail.com> <nova@u4>
|
Nachi Ueno <ueno.nachi@lab.ntt.co.jp> <openstack@lab.ntt.co.jp> <nati.ueno@gmail.com> <nova@u4>
|
||||||
Paul Voccio <paul@openstack.org>
|
Paul Voccio <paul@openstack.org>
|
||||||
Rick Clark <rick@openstack.org>
|
Rick Clark <rick@openstack.org>
|
||||||
|
@ -62,7 +62,6 @@ import time
|
|||||||
|
|
||||||
import IPy
|
import IPy
|
||||||
|
|
||||||
|
|
||||||
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||||
@ -82,9 +81,8 @@ from nova import log as logging
|
|||||||
from nova import quota
|
from nova import quota
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import manager
|
from nova.auth import manager
|
||||||
from nova import rpc
|
|
||||||
from nova.cloudpipe import pipelib
|
from nova.cloudpipe import pipelib
|
||||||
from nova.api.ec2 import cloud
|
|
||||||
|
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
@ -467,82 +465,6 @@ class NetworkCommands(object):
|
|||||||
int(vpn_start), fixed_range_v6)
|
int(vpn_start), fixed_range_v6)
|
||||||
|
|
||||||
|
|
||||||
class InstanceCommands(object):
|
|
||||||
"""Class for mangaging VM instances."""
|
|
||||||
|
|
||||||
def live_migration(self, ec2_id, dest):
|
|
||||||
"""live_migration"""
|
|
||||||
|
|
||||||
ctxt = context.get_admin_context()
|
|
||||||
instance_id = cloud.ec2_id_to_id(ec2_id)
|
|
||||||
|
|
||||||
if FLAGS.connection_type != 'libvirt':
|
|
||||||
msg = _('Only KVM is supported for now. Sorry!')
|
|
||||||
raise exception.Error(msg)
|
|
||||||
|
|
||||||
if FLAGS.volume_driver != 'nova.volume.driver.AOEDriver':
|
|
||||||
instance_ref = db.instance_get(ctxt, instance_id)
|
|
||||||
if len(instance_ref['volumes']) != 0:
|
|
||||||
msg = _(("""Volumes attached by ISCSIDriver"""
|
|
||||||
""" are not supported. Sorry!"""))
|
|
||||||
raise exception.Error(msg)
|
|
||||||
|
|
||||||
rpc.call(ctxt,
|
|
||||||
FLAGS.scheduler_topic,
|
|
||||||
{"method": "live_migration",
|
|
||||||
"args": {"instance_id": instance_id,
|
|
||||||
"dest": dest,
|
|
||||||
"topic": FLAGS.compute_topic}})
|
|
||||||
|
|
||||||
msg = 'Migration of %s initiated. ' % ec2_id
|
|
||||||
msg += 'Check its progress using euca-describe-instances.'
|
|
||||||
print msg
|
|
||||||
|
|
||||||
|
|
||||||
class HostCommands(object):
|
|
||||||
"""Class for mangaging host(physical nodes)."""
|
|
||||||
|
|
||||||
def list(self):
|
|
||||||
"""describe host list."""
|
|
||||||
|
|
||||||
# To supress msg: No handlers could be found for logger "amqplib"
|
|
||||||
logging.basicConfig()
|
|
||||||
|
|
||||||
service_refs = db.service_get_all(context.get_admin_context())
|
|
||||||
hosts = [h['host'] for h in service_refs]
|
|
||||||
hosts = list(set(hosts))
|
|
||||||
for host in hosts:
|
|
||||||
print host
|
|
||||||
|
|
||||||
def show(self, host):
|
|
||||||
"""describe cpu/memory/hdd info for host."""
|
|
||||||
|
|
||||||
result = rpc.call(context.get_admin_context(),
|
|
||||||
FLAGS.scheduler_topic,
|
|
||||||
{"method": "show_host_resource",
|
|
||||||
"args": {"host": host}})
|
|
||||||
|
|
||||||
# Checking result msg format is necessary, that will have done
|
|
||||||
# when this feture is included in API.
|
|
||||||
if type(result) != dict:
|
|
||||||
print 'Unexpected error occurs'
|
|
||||||
elif not result['ret']:
|
|
||||||
print '%s' % result['msg']
|
|
||||||
else:
|
|
||||||
cpu = result['phy_resource']['vcpus']
|
|
||||||
mem = result['phy_resource']['memory_mb']
|
|
||||||
hdd = result['phy_resource']['local_gb']
|
|
||||||
|
|
||||||
print 'HOST\t\tPROJECT\t\tcpu\tmem(mb)\tdisk(gb)'
|
|
||||||
print '%s\t\t\t%s\t%s\t%s' % (host, cpu, mem, hdd)
|
|
||||||
for p_id, val in result['usage'].items():
|
|
||||||
print '%s\t%s\t\t%s\t%s\t%s' % (host,
|
|
||||||
p_id,
|
|
||||||
val['vcpus'],
|
|
||||||
val['memory_mb'],
|
|
||||||
val['local_gb'])
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceCommands(object):
|
class ServiceCommands(object):
|
||||||
"""Enable and disable running services"""
|
"""Enable and disable running services"""
|
||||||
|
|
||||||
@ -605,8 +527,6 @@ CATEGORIES = [
|
|||||||
('vpn', VpnCommands),
|
('vpn', VpnCommands),
|
||||||
('floating', FloatingIpCommands),
|
('floating', FloatingIpCommands),
|
||||||
('network', NetworkCommands),
|
('network', NetworkCommands),
|
||||||
('instance', InstanceCommands),
|
|
||||||
('host', HostCommands),
|
|
||||||
('service', ServiceCommands),
|
('service', ServiceCommands),
|
||||||
('log', LogCommands)]
|
('log', LogCommands)]
|
||||||
|
|
||||||
|
@ -729,7 +729,7 @@ class CloudController(object):
|
|||||||
ec2_id = None
|
ec2_id = None
|
||||||
if (floating_ip_ref['fixed_ip']
|
if (floating_ip_ref['fixed_ip']
|
||||||
and floating_ip_ref['fixed_ip']['instance']):
|
and floating_ip_ref['fixed_ip']['instance']):
|
||||||
instance_id = floating_ip_ref['fixed_ip']['instance']['id']
|
instance_id = floating_ip_ref['fixed_ip']['instance']['ec2_id']
|
||||||
ec2_id = id_to_ec2_id(instance_id)
|
ec2_id = id_to_ec2_id(instance_id)
|
||||||
address_rv = {'public_ip': address,
|
address_rv = {'public_ip': address,
|
||||||
'instance_id': ec2_id}
|
'instance_id': ec2_id}
|
||||||
|
@ -41,7 +41,6 @@ import logging
|
|||||||
import socket
|
import socket
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
from nova import db
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
@ -121,35 +120,6 @@ class ComputeManager(manager.Manager):
|
|||||||
"""
|
"""
|
||||||
self.driver.init_host()
|
self.driver.init_host()
|
||||||
|
|
||||||
def update_service(self, ctxt, host, binary):
|
|
||||||
"""Insert compute node specific information to DB."""
|
|
||||||
|
|
||||||
try:
|
|
||||||
service_ref = db.service_get_by_args(ctxt,
|
|
||||||
host,
|
|
||||||
binary)
|
|
||||||
except exception.NotFound:
|
|
||||||
msg = _(("""Cannot insert compute manager specific info"""
|
|
||||||
"""Because no service record found."""))
|
|
||||||
raise exception.Invalid(msg)
|
|
||||||
|
|
||||||
# Updating host information
|
|
||||||
vcpu = self.driver.get_vcpu_number()
|
|
||||||
memory_mb = self.driver.get_memory_mb()
|
|
||||||
local_gb = self.driver.get_local_gb()
|
|
||||||
hypervisor = self.driver.get_hypervisor_type()
|
|
||||||
version = self.driver.get_hypervisor_version()
|
|
||||||
cpu_info = self.driver.get_cpu_info()
|
|
||||||
|
|
||||||
db.service_update(ctxt,
|
|
||||||
service_ref['id'],
|
|
||||||
{'vcpus': vcpu,
|
|
||||||
'memory_mb': memory_mb,
|
|
||||||
'local_gb': local_gb,
|
|
||||||
'hypervisor_type': hypervisor,
|
|
||||||
'hypervisor_version': version,
|
|
||||||
'cpu_info': cpu_info})
|
|
||||||
|
|
||||||
def _update_state(self, context, instance_id):
|
def _update_state(self, context, instance_id):
|
||||||
"""Update the state of an instance from the driver info."""
|
"""Update the state of an instance from the driver info."""
|
||||||
# FIXME(ja): include other fields from state?
|
# FIXME(ja): include other fields from state?
|
||||||
@ -208,10 +178,9 @@ class ComputeManager(manager.Manager):
|
|||||||
raise exception.Error(_("Instance has already been created"))
|
raise exception.Error(_("Instance has already been created"))
|
||||||
LOG.audit(_("instance %s: starting..."), instance_id,
|
LOG.audit(_("instance %s: starting..."), instance_id,
|
||||||
context=context)
|
context=context)
|
||||||
|
|
||||||
self.db.instance_update(context,
|
self.db.instance_update(context,
|
||||||
instance_id,
|
instance_id,
|
||||||
{'host': self.host, 'launched_on': self.host})
|
{'host': self.host})
|
||||||
|
|
||||||
self.db.instance_set_state(context,
|
self.db.instance_set_state(context,
|
||||||
instance_id,
|
instance_id,
|
||||||
@ -591,88 +560,3 @@ class ComputeManager(manager.Manager):
|
|||||||
self.volume_manager.remove_compute_volume(context, volume_id)
|
self.volume_manager.remove_compute_volume(context, volume_id)
|
||||||
self.db.volume_detached(context, volume_id)
|
self.db.volume_detached(context, volume_id)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def compare_cpu(self, context, cpu_info):
|
|
||||||
""" Check the host cpu is compatible to a cpu given by xml."""
|
|
||||||
return self.driver.compare_cpu(cpu_info)
|
|
||||||
|
|
||||||
def pre_live_migration(self, context, instance_id, dest):
|
|
||||||
"""Any preparation for live migration at dst host."""
|
|
||||||
|
|
||||||
# Getting instance info
|
|
||||||
instance_ref = db.instance_get(context, instance_id)
|
|
||||||
ec2_id = instance_ref['hostname']
|
|
||||||
|
|
||||||
# Getting fixed ips
|
|
||||||
fixed_ip = db.instance_get_fixed_address(context, instance_id)
|
|
||||||
if not fixed_ip:
|
|
||||||
msg = _('%s(%s) doesnt have fixed_ip') % (instance_id, ec2_id)
|
|
||||||
raise exception.NotFound(msg)
|
|
||||||
|
|
||||||
# If any volume is mounted, prepare here.
|
|
||||||
if len(instance_ref['volumes']) == 0:
|
|
||||||
logging.info(_("%s has no volume.") % ec2_id)
|
|
||||||
else:
|
|
||||||
for v in instance_ref['volumes']:
|
|
||||||
self.volume_manager.setup_compute_volume(context, v['id'])
|
|
||||||
|
|
||||||
# Bridge settings
|
|
||||||
# call this method prior to ensure_filtering_rules_for_instance,
|
|
||||||
# since bridge is not set up, ensure_filtering_rules_for instance
|
|
||||||
# fails.
|
|
||||||
self.network_manager.setup_compute_network(context, instance_id)
|
|
||||||
|
|
||||||
# Creating filters to hypervisors and firewalls.
|
|
||||||
# An example is that nova-instance-instance-xxx,
|
|
||||||
# which is written to libvirt.xml( check "virsh nwfilter-list )
|
|
||||||
# On destination host, this nwfilter is necessary.
|
|
||||||
# In addition, this method is creating filtering rule
|
|
||||||
# onto destination host.
|
|
||||||
self.driver.ensure_filtering_rules_for_instance(instance_ref)
|
|
||||||
|
|
||||||
def live_migration(self, context, instance_id, dest):
|
|
||||||
"""executes live migration."""
|
|
||||||
|
|
||||||
# Get instance for error handling.
|
|
||||||
instance_ref = db.instance_get(context, instance_id)
|
|
||||||
ec2_id = instance_ref['hostname']
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Checking volume node is working correctly when any volumes
|
|
||||||
# are attached to instances.
|
|
||||||
if len(instance_ref['volumes']) != 0:
|
|
||||||
rpc.call(context,
|
|
||||||
FLAGS.volume_topic,
|
|
||||||
{"method": "check_for_export",
|
|
||||||
"args": {'instance_id': instance_id}})
|
|
||||||
|
|
||||||
# Asking dest host to preparing live migration.
|
|
||||||
compute_topic = db.queue_get_for(context,
|
|
||||||
FLAGS.compute_topic,
|
|
||||||
dest)
|
|
||||||
rpc.call(context,
|
|
||||||
compute_topic,
|
|
||||||
{"method": "pre_live_migration",
|
|
||||||
"args": {'instance_id': instance_id,
|
|
||||||
'dest': dest}})
|
|
||||||
|
|
||||||
except Exception, e:
|
|
||||||
msg = _('Pre live migration for %s failed at %s')
|
|
||||||
logging.error(msg, ec2_id, dest)
|
|
||||||
db.instance_set_state(context,
|
|
||||||
instance_id,
|
|
||||||
power_state.RUNNING,
|
|
||||||
'running')
|
|
||||||
|
|
||||||
for v in instance_ref['volumes']:
|
|
||||||
db.volume_update(context,
|
|
||||||
v['id'],
|
|
||||||
{'status': 'in-use'})
|
|
||||||
|
|
||||||
# e should be raised. just calling "raise" may raise NotFound.
|
|
||||||
raise e
|
|
||||||
|
|
||||||
# Executing live migration
|
|
||||||
# live_migration might raises exceptions, but
|
|
||||||
# nothing must be recovered in this version.
|
|
||||||
self.driver.live_migration(context, instance_ref, dest)
|
|
||||||
|
@ -253,10 +253,6 @@ def floating_ip_get_by_address(context, address):
|
|||||||
return IMPL.floating_ip_get_by_address(context, address)
|
return IMPL.floating_ip_get_by_address(context, address)
|
||||||
|
|
||||||
|
|
||||||
def floating_ip_update(context, address, values):
|
|
||||||
"""update floating ip information."""
|
|
||||||
return IMPL.floating_ip_update(context, address, values)
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
||||||
|
|
||||||
@ -409,32 +405,6 @@ def instance_add_security_group(context, instance_id, security_group_id):
|
|||||||
security_group_id)
|
security_group_id)
|
||||||
|
|
||||||
|
|
||||||
def instance_get_all_by_host(context, hostname):
|
|
||||||
"""Get instances by host"""
|
|
||||||
return IMPL.instance_get_all_by_host(context, hostname)
|
|
||||||
|
|
||||||
|
|
||||||
def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id):
|
|
||||||
"""Get instances.vcpus by host and project"""
|
|
||||||
return IMPL.instance_get_vcpu_sum_by_host_and_project(context,
|
|
||||||
hostname,
|
|
||||||
proj_id)
|
|
||||||
|
|
||||||
|
|
||||||
def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id):
|
|
||||||
"""Get amount of memory by host and project """
|
|
||||||
return IMPL.instance_get_memory_sum_by_host_and_project(context,
|
|
||||||
hostname,
|
|
||||||
proj_id)
|
|
||||||
|
|
||||||
|
|
||||||
def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id):
|
|
||||||
"""Get total amount of disk by host and project """
|
|
||||||
return IMPL.instance_get_disk_sum_by_host_and_project(context,
|
|
||||||
hostname,
|
|
||||||
proj_id)
|
|
||||||
|
|
||||||
|
|
||||||
def instance_action_create(context, values):
|
def instance_action_create(context, values):
|
||||||
"""Create an instance action from the values dictionary."""
|
"""Create an instance action from the values dictionary."""
|
||||||
return IMPL.instance_action_create(context, values)
|
return IMPL.instance_action_create(context, values)
|
||||||
|
@ -495,16 +495,6 @@ def floating_ip_get_by_address(context, address, session=None):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def floating_ip_update(context, address, values):
|
|
||||||
session = get_session()
|
|
||||||
with session.begin():
|
|
||||||
floating_ip_ref = floating_ip_get_by_address(context, address, session)
|
|
||||||
for (key, value) in values.iteritems():
|
|
||||||
floating_ip_ref[key] = value
|
|
||||||
floating_ip_ref.save(session=session)
|
|
||||||
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
|
|
||||||
|
|
||||||
@ -868,7 +858,6 @@ def instance_update(context, instance_id, values):
|
|||||||
return instance_ref
|
return instance_ref
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def instance_add_security_group(context, instance_id, security_group_id):
|
def instance_add_security_group(context, instance_id, security_group_id):
|
||||||
"""Associate the given security group with the given instance"""
|
"""Associate the given security group with the given instance"""
|
||||||
session = get_session()
|
session = get_session()
|
||||||
@ -881,59 +870,6 @@ def instance_add_security_group(context, instance_id, security_group_id):
|
|||||||
instance_ref.save(session=session)
|
instance_ref.save(session=session)
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def instance_get_all_by_host(context, hostname):
|
|
||||||
session = get_session()
|
|
||||||
if not session:
|
|
||||||
session = get_session()
|
|
||||||
|
|
||||||
result = session.query(models.Instance).\
|
|
||||||
filter_by(host=hostname).\
|
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
|
||||||
all()
|
|
||||||
if not result:
|
|
||||||
return []
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def _instance_get_sum_by_host_and_project(context, column, hostname, proj_id):
|
|
||||||
session = get_session()
|
|
||||||
|
|
||||||
result = session.query(models.Instance).\
|
|
||||||
filter_by(host=hostname).\
|
|
||||||
filter_by(project_id=proj_id).\
|
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
|
||||||
value(column)
|
|
||||||
if not result:
|
|
||||||
return 0
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id):
|
|
||||||
return _instance_get_sum_by_host_and_project(context,
|
|
||||||
'vcpus',
|
|
||||||
hostname,
|
|
||||||
proj_id)
|
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id):
|
|
||||||
return _instance_get_sum_by_host_and_project(context,
|
|
||||||
'memory_mb',
|
|
||||||
hostname,
|
|
||||||
proj_id)
|
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id):
|
|
||||||
return _instance_get_sum_by_host_and_project(context,
|
|
||||||
'local_gb',
|
|
||||||
hostname,
|
|
||||||
proj_id)
|
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
def instance_action_create(context, values):
|
def instance_action_create(context, values):
|
||||||
"""Create an instance action from the values dictionary."""
|
"""Create an instance action from the values dictionary."""
|
||||||
|
@ -150,32 +150,13 @@ class Service(BASE, NovaBase):
|
|||||||
|
|
||||||
__tablename__ = 'services'
|
__tablename__ = 'services'
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
#host_id = Column(Integer, ForeignKey('hosts.id'), nullable=True)
|
host = Column(String(255)) # , ForeignKey('hosts.id'))
|
||||||
#host = relationship(Host, backref=backref('services'))
|
|
||||||
host = Column(String(255))
|
|
||||||
binary = Column(String(255))
|
binary = Column(String(255))
|
||||||
topic = Column(String(255))
|
topic = Column(String(255))
|
||||||
report_count = Column(Integer, nullable=False, default=0)
|
report_count = Column(Integer, nullable=False, default=0)
|
||||||
disabled = Column(Boolean, default=False)
|
disabled = Column(Boolean, default=False)
|
||||||
availability_zone = Column(String(255), default='nova')
|
availability_zone = Column(String(255), default='nova')
|
||||||
|
|
||||||
# The below items are compute node only.
|
|
||||||
# -1 or None is inserted for other service.
|
|
||||||
vcpus = Column(Integer, nullable=False, default=-1)
|
|
||||||
memory_mb = Column(Integer, nullable=False, default=-1)
|
|
||||||
local_gb = Column(Integer, nullable=False, default=-1)
|
|
||||||
hypervisor_type = Column(String(128))
|
|
||||||
hypervisor_version = Column(Integer, nullable=False, default=-1)
|
|
||||||
# Note(masumotok): Expected Strings example:
|
|
||||||
#
|
|
||||||
# '{"arch":"x86_64", "model":"Nehalem",
|
|
||||||
# "topology":{"sockets":1, "threads":2, "cores":3},
|
|
||||||
# features:[ "tdtscp", "xtpr"]}'
|
|
||||||
#
|
|
||||||
# Points are "json translatable" and it must have all
|
|
||||||
# dictionary keys above.
|
|
||||||
cpu_info = Column(String(512))
|
|
||||||
|
|
||||||
|
|
||||||
class Certificate(BASE, NovaBase):
|
class Certificate(BASE, NovaBase):
|
||||||
"""Represents a an x509 certificate"""
|
"""Represents a an x509 certificate"""
|
||||||
@ -250,9 +231,6 @@ class Instance(BASE, NovaBase):
|
|||||||
display_name = Column(String(255))
|
display_name = Column(String(255))
|
||||||
display_description = Column(String(255))
|
display_description = Column(String(255))
|
||||||
|
|
||||||
# To remember on which host a instance booted.
|
|
||||||
# An instance may moved to other host by live migraiton.
|
|
||||||
launched_on = Column(String(255))
|
|
||||||
locked = Column(Boolean)
|
locked = Column(Boolean)
|
||||||
|
|
||||||
# TODO(vish): see Ewan's email about state improvements, probably
|
# TODO(vish): see Ewan's email about state improvements, probably
|
||||||
@ -610,7 +588,7 @@ def register_models():
|
|||||||
Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp,
|
Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp,
|
||||||
Network, SecurityGroup, SecurityGroupIngressRule,
|
Network, SecurityGroup, SecurityGroupIngressRule,
|
||||||
SecurityGroupInstanceAssociation, AuthToken, User,
|
SecurityGroupInstanceAssociation, AuthToken, User,
|
||||||
Project, Certificate, ConsolePool, Console) # , Host, Image
|
Project, Certificate, ConsolePool, Console) # , Image, Host
|
||||||
engine = create_engine(FLAGS.sql_connection, echo=False)
|
engine = create_engine(FLAGS.sql_connection, echo=False)
|
||||||
for model in models:
|
for model in models:
|
||||||
model.metadata.create_all(engine)
|
model.metadata.create_all(engine)
|
||||||
|
@ -159,7 +159,7 @@ class NetworkManager(manager.Manager):
|
|||||||
"""Called when this host becomes the host for a network."""
|
"""Called when this host becomes the host for a network."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def setup_compute_network(self, context, instance_id, network_ref=None):
|
def setup_compute_network(self, context, instance_id):
|
||||||
"""Sets up matching network for compute hosts."""
|
"""Sets up matching network for compute hosts."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -320,7 +320,7 @@ class FlatManager(NetworkManager):
|
|||||||
self.db.fixed_ip_update(context, address, {'allocated': False})
|
self.db.fixed_ip_update(context, address, {'allocated': False})
|
||||||
self.db.fixed_ip_disassociate(context.elevated(), address)
|
self.db.fixed_ip_disassociate(context.elevated(), address)
|
||||||
|
|
||||||
def setup_compute_network(self, context, instance_id, network_ref=None):
|
def setup_compute_network(self, context, instance_id):
|
||||||
"""Network is created manually."""
|
"""Network is created manually."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -395,10 +395,9 @@ class FlatDHCPManager(FlatManager):
|
|||||||
super(FlatDHCPManager, self).init_host()
|
super(FlatDHCPManager, self).init_host()
|
||||||
self.driver.metadata_forward()
|
self.driver.metadata_forward()
|
||||||
|
|
||||||
def setup_compute_network(self, context, instance_id, network_ref=None):
|
def setup_compute_network(self, context, instance_id):
|
||||||
"""Sets up matching network for compute hosts."""
|
"""Sets up matching network for compute hosts."""
|
||||||
if network_ref is None:
|
network_ref = db.network_get_by_instance(context, instance_id)
|
||||||
network_ref = db.network_get_by_instance(context, instance_id)
|
|
||||||
self.driver.ensure_bridge(network_ref['bridge'],
|
self.driver.ensure_bridge(network_ref['bridge'],
|
||||||
FLAGS.flat_interface)
|
FLAGS.flat_interface)
|
||||||
|
|
||||||
@ -488,10 +487,9 @@ class VlanManager(NetworkManager):
|
|||||||
"""Returns a fixed ip to the pool."""
|
"""Returns a fixed ip to the pool."""
|
||||||
self.db.fixed_ip_update(context, address, {'allocated': False})
|
self.db.fixed_ip_update(context, address, {'allocated': False})
|
||||||
|
|
||||||
def setup_compute_network(self, context, instance_id, network_ref=None):
|
def setup_compute_network(self, context, instance_id):
|
||||||
"""Sets up matching network for compute hosts."""
|
"""Sets up matching network for compute hosts."""
|
||||||
if network_ref is None:
|
network_ref = db.network_get_by_instance(context, instance_id)
|
||||||
network_ref = db.network_get_by_instance(context, instance_id)
|
|
||||||
self.driver.ensure_vlan_bridge(network_ref['vlan'],
|
self.driver.ensure_vlan_bridge(network_ref['vlan'],
|
||||||
network_ref['bridge'])
|
network_ref['bridge'])
|
||||||
|
|
||||||
|
@ -26,9 +26,6 @@ import datetime
|
|||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import log as logging
|
|
||||||
from nova import rpc
|
|
||||||
from nova.compute import power_state
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
flags.DEFINE_integer('service_down_time', 60,
|
flags.DEFINE_integer('service_down_time', 60,
|
||||||
@ -67,183 +64,3 @@ class Scheduler(object):
|
|||||||
def schedule(self, context, topic, *_args, **_kwargs):
|
def schedule(self, context, topic, *_args, **_kwargs):
|
||||||
"""Must override at least this method for scheduler to work."""
|
"""Must override at least this method for scheduler to work."""
|
||||||
raise NotImplementedError(_("Must implement a fallback schedule"))
|
raise NotImplementedError(_("Must implement a fallback schedule"))
|
||||||
|
|
||||||
def schedule_live_migration(self, context, instance_id, dest):
|
|
||||||
""" live migration method """
|
|
||||||
|
|
||||||
# Whether instance exists and running
|
|
||||||
instance_ref = db.instance_get(context, instance_id)
|
|
||||||
ec2_id = instance_ref['hostname']
|
|
||||||
|
|
||||||
# Checking instance.
|
|
||||||
self._live_migration_src_check(context, instance_ref)
|
|
||||||
|
|
||||||
# Checking destination host.
|
|
||||||
self._live_migration_dest_check(context, instance_ref, dest)
|
|
||||||
|
|
||||||
# Common checking.
|
|
||||||
self._live_migration_common_check(context, instance_ref, dest)
|
|
||||||
|
|
||||||
# Changing instance_state.
|
|
||||||
db.instance_set_state(context,
|
|
||||||
instance_id,
|
|
||||||
power_state.PAUSED,
|
|
||||||
'migrating')
|
|
||||||
|
|
||||||
# Changing volume state
|
|
||||||
for v in instance_ref['volumes']:
|
|
||||||
db.volume_update(context,
|
|
||||||
v['id'],
|
|
||||||
{'status': 'migrating'})
|
|
||||||
|
|
||||||
# Return value is necessary to send request to src
|
|
||||||
# Check _schedule() in detail.
|
|
||||||
src = instance_ref['host']
|
|
||||||
return src
|
|
||||||
|
|
||||||
def _live_migration_src_check(self, context, instance_ref):
|
|
||||||
"""Live migration check routine (for src host)"""
|
|
||||||
|
|
||||||
# Checking instance is running.
|
|
||||||
if power_state.RUNNING != instance_ref['state'] or \
|
|
||||||
'running' != instance_ref['state_description']:
|
|
||||||
msg = _('Instance(%s) is not running')
|
|
||||||
ec2_id = instance_ref['hostname']
|
|
||||||
raise exception.Invalid(msg % ec2_id)
|
|
||||||
|
|
||||||
# Checing volume node is running when any volumes are mounted
|
|
||||||
# to the instance.
|
|
||||||
if len(instance_ref['volumes']) != 0:
|
|
||||||
services = db.service_get_all_by_topic(context, 'volume')
|
|
||||||
if len(services) < 1 or not self.service_is_up(services[0]):
|
|
||||||
msg = _('volume node is not alive(time synchronize problem?)')
|
|
||||||
raise exception.Invalid(msg)
|
|
||||||
|
|
||||||
# Checking src host is alive.
|
|
||||||
src = instance_ref['host']
|
|
||||||
services = db.service_get_all_by_topic(context, 'compute')
|
|
||||||
services = [service for service in services if service.host == src]
|
|
||||||
if len(services) < 1 or not self.service_is_up(services[0]):
|
|
||||||
msg = _('%s is not alive(time synchronize problem?)')
|
|
||||||
raise exception.Invalid(msg % src)
|
|
||||||
|
|
||||||
def _live_migration_dest_check(self, context, instance_ref, dest):
|
|
||||||
"""Live migration check routine (for destination host)"""
|
|
||||||
|
|
||||||
# Checking dest exists and compute node.
|
|
||||||
dservice_refs = db.service_get_all_by_host(context, dest)
|
|
||||||
if len(dservice_refs) <= 0:
|
|
||||||
msg = _('%s does not exists.')
|
|
||||||
raise exception.Invalid(msg % dest)
|
|
||||||
|
|
||||||
dservice_ref = dservice_refs[0]
|
|
||||||
if dservice_ref['topic'] != 'compute':
|
|
||||||
msg = _('%s must be compute node')
|
|
||||||
raise exception.Invalid(msg % dest)
|
|
||||||
|
|
||||||
# Checking dest host is alive.
|
|
||||||
if not self.service_is_up(dservice_ref):
|
|
||||||
msg = _('%s is not alive(time synchronize problem?)')
|
|
||||||
raise exception.Invalid(msg % dest)
|
|
||||||
|
|
||||||
# Checking whether The host where instance is running
|
|
||||||
# and dest is not same.
|
|
||||||
src = instance_ref['host']
|
|
||||||
if dest == src:
|
|
||||||
ec2_id = instance_ref['hostname']
|
|
||||||
msg = _('%s is where %s is running now. choose other host.')
|
|
||||||
raise exception.Invalid(msg % (dest, ec2_id))
|
|
||||||
|
|
||||||
# Checking dst host still has enough capacities.
|
|
||||||
self.has_enough_resource(context, instance_ref, dest)
|
|
||||||
|
|
||||||
def _live_migration_common_check(self, context, instance_ref, dest):
|
|
||||||
"""
|
|
||||||
Live migration check routine.
|
|
||||||
Below pre-checkings are followed by
|
|
||||||
http://wiki.libvirt.org/page/TodoPreMigrationChecks
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Checking dest exists.
|
|
||||||
dservice_refs = db.service_get_all_by_host(context, dest)
|
|
||||||
if len(dservice_refs) <= 0:
|
|
||||||
msg = _('%s does not exists.')
|
|
||||||
raise exception.Invalid(msg % dest)
|
|
||||||
dservice_ref = dservice_refs[0]
|
|
||||||
|
|
||||||
# Checking original host( where instance was launched at) exists.
|
|
||||||
orighost = instance_ref['launched_on']
|
|
||||||
oservice_refs = db.service_get_all_by_host(context, orighost)
|
|
||||||
if len(oservice_refs) <= 0:
|
|
||||||
msg = _('%s(where instance was launched at) does not exists.')
|
|
||||||
raise exception.Invalid(msg % orighost)
|
|
||||||
oservice_ref = oservice_refs[0]
|
|
||||||
|
|
||||||
# Checking hypervisor is same.
|
|
||||||
otype = oservice_ref['hypervisor_type']
|
|
||||||
dtype = dservice_ref['hypervisor_type']
|
|
||||||
if otype != dtype:
|
|
||||||
msg = _('Different hypervisor type(%s->%s)')
|
|
||||||
raise exception.Invalid(msg % (otype, dtype))
|
|
||||||
|
|
||||||
# Checkng hypervisor version.
|
|
||||||
oversion = oservice_ref['hypervisor_version']
|
|
||||||
dversion = dservice_ref['hypervisor_version']
|
|
||||||
if oversion > dversion:
|
|
||||||
msg = _('Older hypervisor version(%s->%s)')
|
|
||||||
raise exception.Invalid(msg % (oversion, dversion))
|
|
||||||
|
|
||||||
# Checking cpuinfo.
|
|
||||||
cpu_info = oservice_ref['cpu_info']
|
|
||||||
try:
|
|
||||||
rpc.call(context,
|
|
||||||
db.queue_get_for(context, FLAGS.compute_topic, dest),
|
|
||||||
{"method": 'compare_cpu',
|
|
||||||
"args": {'cpu_info': cpu_info}})
|
|
||||||
|
|
||||||
except rpc.RemoteError, e:
|
|
||||||
msg = _(("""%s doesnt have compatibility to %s"""
|
|
||||||
"""(where %s was launched at)"""))
|
|
||||||
ec2_id = instance_ref['hostname']
|
|
||||||
src = instance_ref['host']
|
|
||||||
logging.error(msg % (dest, src, ec2_id))
|
|
||||||
raise e
|
|
||||||
|
|
||||||
def has_enough_resource(self, context, instance_ref, dest):
|
|
||||||
""" Check if destination host has enough resource for live migration"""
|
|
||||||
|
|
||||||
# Getting instance information
|
|
||||||
ec2_id = instance_ref['hostname']
|
|
||||||
vcpus = instance_ref['vcpus']
|
|
||||||
mem = instance_ref['memory_mb']
|
|
||||||
hdd = instance_ref['local_gb']
|
|
||||||
|
|
||||||
# Gettin host information
|
|
||||||
service_refs = db.service_get_all_by_host(context, dest)
|
|
||||||
if len(service_refs) <= 0:
|
|
||||||
msg = _('%s does not exists.')
|
|
||||||
raise exception.Invalid(msg % dest)
|
|
||||||
service_ref = service_refs[0]
|
|
||||||
|
|
||||||
total_cpu = int(service_ref['vcpus'])
|
|
||||||
total_mem = int(service_ref['memory_mb'])
|
|
||||||
total_hdd = int(service_ref['local_gb'])
|
|
||||||
|
|
||||||
instances_ref = db.instance_get_all_by_host(context, dest)
|
|
||||||
for i_ref in instances_ref:
|
|
||||||
total_cpu -= int(i_ref['vcpus'])
|
|
||||||
total_mem -= int(i_ref['memory_mb'])
|
|
||||||
total_hdd -= int(i_ref['local_gb'])
|
|
||||||
|
|
||||||
# Checking host has enough information
|
|
||||||
logging.debug('host(%s) remains vcpu:%s mem:%s hdd:%s,' %
|
|
||||||
(dest, total_cpu, total_mem, total_hdd))
|
|
||||||
logging.debug('instance(%s) has vcpu:%s mem:%s hdd:%s,' %
|
|
||||||
(ec2_id, vcpus, mem, hdd))
|
|
||||||
|
|
||||||
if total_cpu <= vcpus or total_mem <= mem or total_hdd <= hdd:
|
|
||||||
msg = '%s doesnt have enough resource for %s' % (dest, ec2_id)
|
|
||||||
raise exception.NotEmpty(msg)
|
|
||||||
|
|
||||||
logging.debug(_('%s has_enough_resource() for %s') % (dest, ec2_id))
|
|
||||||
|
@ -29,7 +29,6 @@ from nova import log as logging
|
|||||||
from nova import manager
|
from nova import manager
|
||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova import exception
|
|
||||||
|
|
||||||
LOG = logging.getLogger('nova.scheduler.manager')
|
LOG = logging.getLogger('nova.scheduler.manager')
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
@ -68,50 +67,3 @@ class SchedulerManager(manager.Manager):
|
|||||||
{"method": method,
|
{"method": method,
|
||||||
"args": kwargs})
|
"args": kwargs})
|
||||||
LOG.debug(_("Casting to %s %s for %s"), topic, host, method)
|
LOG.debug(_("Casting to %s %s for %s"), topic, host, method)
|
||||||
|
|
||||||
# NOTE (masumotok) : This method should be moved to nova.api.ec2.admin.
|
|
||||||
# Based on bear design summit discussion,
|
|
||||||
# just put this here for bexar release.
|
|
||||||
def show_host_resource(self, context, host, *args):
|
|
||||||
""" show the physical/usage resource given by hosts."""
|
|
||||||
|
|
||||||
services = db.service_get_all_by_host(context, host)
|
|
||||||
if len(services) == 0:
|
|
||||||
return {'ret': False, 'msg': 'No such Host'}
|
|
||||||
|
|
||||||
compute = [s for s in services if s['topic'] == 'compute']
|
|
||||||
if 0 == len(compute):
|
|
||||||
service_ref = services[0]
|
|
||||||
else:
|
|
||||||
service_ref = compute[0]
|
|
||||||
|
|
||||||
# Getting physical resource information
|
|
||||||
h_resource = {'vcpus': service_ref['vcpus'],
|
|
||||||
'memory_mb': service_ref['memory_mb'],
|
|
||||||
'local_gb': service_ref['local_gb']}
|
|
||||||
|
|
||||||
# Getting usage resource information
|
|
||||||
u_resource = {}
|
|
||||||
instances_ref = db.instance_get_all_by_host(context,
|
|
||||||
service_ref['host'])
|
|
||||||
|
|
||||||
if 0 == len(instances_ref):
|
|
||||||
return {'ret': True, 'phy_resource': h_resource, 'usage': {}}
|
|
||||||
|
|
||||||
project_ids = [i['project_id'] for i in instances_ref]
|
|
||||||
project_ids = list(set(project_ids))
|
|
||||||
for p_id in project_ids:
|
|
||||||
vcpus = db.instance_get_vcpu_sum_by_host_and_project(context,
|
|
||||||
host,
|
|
||||||
p_id)
|
|
||||||
mem = db.instance_get_memory_sum_by_host_and_project(context,
|
|
||||||
host,
|
|
||||||
p_id)
|
|
||||||
hdd = db.instance_get_disk_sum_by_host_and_project(context,
|
|
||||||
host,
|
|
||||||
p_id)
|
|
||||||
u_resource[p_id] = {'vcpus': vcpus,
|
|
||||||
'memory_mb': mem,
|
|
||||||
'local_gb': hdd}
|
|
||||||
|
|
||||||
return {'ret': True, 'phy_resource': h_resource, 'usage': u_resource}
|
|
||||||
|
@ -80,7 +80,6 @@ class Service(object):
|
|||||||
self.manager.init_host()
|
self.manager.init_host()
|
||||||
self.model_disconnected = False
|
self.model_disconnected = False
|
||||||
ctxt = context.get_admin_context()
|
ctxt = context.get_admin_context()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
service_ref = db.service_get_by_args(ctxt,
|
service_ref = db.service_get_by_args(ctxt,
|
||||||
self.host,
|
self.host,
|
||||||
@ -89,9 +88,6 @@ class Service(object):
|
|||||||
except exception.NotFound:
|
except exception.NotFound:
|
||||||
self._create_service_ref(ctxt)
|
self._create_service_ref(ctxt)
|
||||||
|
|
||||||
if 'nova-compute' == self.binary:
|
|
||||||
self.manager.update_service(ctxt, self.host, self.binary)
|
|
||||||
|
|
||||||
conn1 = rpc.Connection.instance(new=True)
|
conn1 = rpc.Connection.instance(new=True)
|
||||||
conn2 = rpc.Connection.instance(new=True)
|
conn2 = rpc.Connection.instance(new=True)
|
||||||
if self.report_interval:
|
if self.report_interval:
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<cpu>
|
|
||||||
<arch>$arch</arch>
|
|
||||||
<model>$model</model>
|
|
||||||
<vendor>$vendor</vendor>
|
|
||||||
<topology sockets="$topology.sockets" cores="$topology.cores" threads="$topology.threads"/>
|
|
||||||
#for $var in $features
|
|
||||||
<features name="$var" />
|
|
||||||
#end for
|
|
||||||
</cpu>
|
|
@ -310,38 +310,6 @@ class FakeConnection(object):
|
|||||||
'username': 'fakeuser',
|
'username': 'fakeuser',
|
||||||
'password': 'fakepassword'}
|
'password': 'fakepassword'}
|
||||||
|
|
||||||
def get_cpu_info(self):
|
|
||||||
"""This method is supported only libvirt. """
|
|
||||||
return
|
|
||||||
|
|
||||||
def get_vcpu_number(self):
|
|
||||||
"""This method is supported only libvirt. """
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def get_memory_mb(self):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def get_local_gb(self):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def get_hypervisor_type(self):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
return
|
|
||||||
|
|
||||||
def get_hypervisor_version(self):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def compare_cpu(self, xml):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
raise NotImplementedError('This method is supported only libvirt.')
|
|
||||||
|
|
||||||
def live_migration(self, context, instance_ref, dest):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
raise NotImplementedError('This method is supported only libvirt.')
|
|
||||||
|
|
||||||
|
|
||||||
class FakeInstance(object):
|
class FakeInstance(object):
|
||||||
|
|
||||||
|
@ -36,11 +36,8 @@ Supports KVM, QEMU, UML, and XEN.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import re
|
|
||||||
import time
|
|
||||||
import random
|
import random
|
||||||
import subprocess
|
import subprocess
|
||||||
import uuid
|
import uuid
|
||||||
@ -83,9 +80,6 @@ flags.DEFINE_string('injected_network_template',
|
|||||||
flags.DEFINE_string('libvirt_xml_template',
|
flags.DEFINE_string('libvirt_xml_template',
|
||||||
utils.abspath('virt/libvirt.xml.template'),
|
utils.abspath('virt/libvirt.xml.template'),
|
||||||
'Libvirt XML Template')
|
'Libvirt XML Template')
|
||||||
flags.DEFINE_string('cpuinfo_xml_template',
|
|
||||||
utils.abspath('virt/cpuinfo.xml.template'),
|
|
||||||
'CpuInfo XML Template (used only live migration now)')
|
|
||||||
flags.DEFINE_string('libvirt_type',
|
flags.DEFINE_string('libvirt_type',
|
||||||
'kvm',
|
'kvm',
|
||||||
'Libvirt domain type (valid options are: '
|
'Libvirt domain type (valid options are: '
|
||||||
@ -94,16 +88,6 @@ flags.DEFINE_string('libvirt_uri',
|
|||||||
'',
|
'',
|
||||||
'Override the default libvirt URI (which is dependent'
|
'Override the default libvirt URI (which is dependent'
|
||||||
' on libvirt_type)')
|
' on libvirt_type)')
|
||||||
flags.DEFINE_string('live_migration_uri',
|
|
||||||
"qemu+tcp://%s/system",
|
|
||||||
'Define protocol used by live_migration feature')
|
|
||||||
flags.DEFINE_string('live_migration_flag',
|
|
||||||
"VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER",
|
|
||||||
'Define live migration behavior.')
|
|
||||||
flags.DEFINE_integer('live_migration_bandwidth', 0,
|
|
||||||
'Define live migration behavior')
|
|
||||||
flags.DEFINE_string('live_migration_timeout_sec', 10,
|
|
||||||
'Timeout second for pre_live_migration is completed.')
|
|
||||||
flags.DEFINE_bool('allow_project_net_traffic',
|
flags.DEFINE_bool('allow_project_net_traffic',
|
||||||
True,
|
True,
|
||||||
'Whether to allow in project network traffic')
|
'Whether to allow in project network traffic')
|
||||||
@ -162,7 +146,6 @@ class LibvirtConnection(object):
|
|||||||
self.libvirt_uri = self.get_uri()
|
self.libvirt_uri = self.get_uri()
|
||||||
|
|
||||||
self.libvirt_xml = open(FLAGS.libvirt_xml_template).read()
|
self.libvirt_xml = open(FLAGS.libvirt_xml_template).read()
|
||||||
self.cpuinfo_xml = open(FLAGS.cpuinfo_xml_template).read()
|
|
||||||
self._wrapped_conn = None
|
self._wrapped_conn = None
|
||||||
self.read_only = read_only
|
self.read_only = read_only
|
||||||
|
|
||||||
@ -835,74 +818,6 @@ class LibvirtConnection(object):
|
|||||||
|
|
||||||
return interfaces
|
return interfaces
|
||||||
|
|
||||||
def get_vcpu_number(self):
|
|
||||||
""" Get vcpu number of physical computer. """
|
|
||||||
return self._conn.getMaxVcpus(None)
|
|
||||||
|
|
||||||
def get_memory_mb(self):
|
|
||||||
"""Get the memory size of physical computer ."""
|
|
||||||
meminfo = open('/proc/meminfo').read().split()
|
|
||||||
idx = meminfo.index('MemTotal:')
|
|
||||||
# transforming kb to mb.
|
|
||||||
return int(meminfo[idx + 1]) / 1024
|
|
||||||
|
|
||||||
def get_local_gb(self):
|
|
||||||
"""Get the hdd size of physical computer ."""
|
|
||||||
hddinfo = os.statvfs(FLAGS.instances_path)
|
|
||||||
return hddinfo.f_bsize * hddinfo.f_blocks / 1024 / 1024 / 1024
|
|
||||||
|
|
||||||
def get_hypervisor_type(self):
|
|
||||||
""" Get hypervisor type """
|
|
||||||
return self._conn.getType()
|
|
||||||
|
|
||||||
def get_hypervisor_version(self):
|
|
||||||
""" Get hypervisor version """
|
|
||||||
return self._conn.getVersion()
|
|
||||||
|
|
||||||
def get_cpu_info(self):
|
|
||||||
""" Get cpuinfo information """
|
|
||||||
xmlstr = self._conn.getCapabilities()
|
|
||||||
xml = libxml2.parseDoc(xmlstr)
|
|
||||||
nodes = xml.xpathEval('//cpu')
|
|
||||||
if len(nodes) != 1:
|
|
||||||
msg = 'Unexpected xml format. tag "cpu" must be 1, but %d.' \
|
|
||||||
% len(nodes)
|
|
||||||
msg += '\n' + xml.serialize()
|
|
||||||
raise exception.Invalid(_(msg))
|
|
||||||
|
|
||||||
arch = xml.xpathEval('//cpu/arch')[0].getContent()
|
|
||||||
model = xml.xpathEval('//cpu/model')[0].getContent()
|
|
||||||
vendor = xml.xpathEval('//cpu/vendor')[0].getContent()
|
|
||||||
|
|
||||||
topology_node = xml.xpathEval('//cpu/topology')[0].get_properties()
|
|
||||||
topology = dict()
|
|
||||||
while topology_node != None:
|
|
||||||
name = topology_node.get_name()
|
|
||||||
topology[name] = topology_node.getContent()
|
|
||||||
topology_node = topology_node.get_next()
|
|
||||||
|
|
||||||
keys = ['cores', 'sockets', 'threads']
|
|
||||||
tkeys = topology.keys()
|
|
||||||
if list(set(tkeys)) != list(set(keys)):
|
|
||||||
msg = _('Invalid xml: topology(%s) must have %s')
|
|
||||||
raise exception.Invalid(msg % (str(topology), ', '.join(keys)))
|
|
||||||
|
|
||||||
feature_nodes = xml.xpathEval('//cpu/feature')
|
|
||||||
features = list()
|
|
||||||
for nodes in feature_nodes:
|
|
||||||
feature_name = nodes.get_properties().getContent()
|
|
||||||
features.append(feature_name)
|
|
||||||
|
|
||||||
template = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """
|
|
||||||
""""topology":{"cores":"%s", "threads":"%s", """
|
|
||||||
""""sockets":"%s"}, "features":[%s]}""")
|
|
||||||
c = topology['cores']
|
|
||||||
s = topology['sockets']
|
|
||||||
t = topology['threads']
|
|
||||||
f = ['"%s"' % x for x in features]
|
|
||||||
cpu_info = template % (arch, model, vendor, c, s, t, ', '.join(f))
|
|
||||||
return cpu_info
|
|
||||||
|
|
||||||
def block_stats(self, instance_name, disk):
|
def block_stats(self, instance_name, disk):
|
||||||
"""
|
"""
|
||||||
Note that this function takes an instance name, not an Instance, so
|
Note that this function takes an instance name, not an Instance, so
|
||||||
@ -933,208 +848,6 @@ class LibvirtConnection(object):
|
|||||||
def refresh_security_group_members(self, security_group_id):
|
def refresh_security_group_members(self, security_group_id):
|
||||||
self.firewall_driver.refresh_security_group_members(security_group_id)
|
self.firewall_driver.refresh_security_group_members(security_group_id)
|
||||||
|
|
||||||
def compare_cpu(self, cpu_info):
|
|
||||||
"""
|
|
||||||
Check the host cpu is compatible to a cpu given by xml.
|
|
||||||
"xml" must be a part of libvirt.openReadonly().getCapabilities().
|
|
||||||
return values follows by virCPUCompareResult.
|
|
||||||
if 0 > return value, do live migration.
|
|
||||||
|
|
||||||
'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult'
|
|
||||||
"""
|
|
||||||
msg = _('Checking cpu_info: instance was launched this cpu.\n: %s ')
|
|
||||||
LOG.info(msg % cpu_info)
|
|
||||||
dic = json.loads(cpu_info)
|
|
||||||
xml = str(Template(self.cpuinfo_xml, searchList=dic))
|
|
||||||
msg = _('to xml...\n: %s ')
|
|
||||||
LOG.info(msg % xml)
|
|
||||||
|
|
||||||
url = 'http://libvirt.org/html/libvirt-libvirt.html'
|
|
||||||
url += '#virCPUCompareResult\n'
|
|
||||||
msg = 'CPU does not have compativility.\n'
|
|
||||||
msg += 'result:%d \n'
|
|
||||||
msg += 'Refer to %s'
|
|
||||||
msg = _(msg)
|
|
||||||
|
|
||||||
# unknown character exists in xml, then libvirt complains
|
|
||||||
try:
|
|
||||||
ret = self._conn.compareCPU(xml, 0)
|
|
||||||
except libvirt.libvirtError, e:
|
|
||||||
LOG.error(msg % (ret, url))
|
|
||||||
raise e
|
|
||||||
|
|
||||||
if ret <= 0:
|
|
||||||
raise exception.Invalid(msg % (ret, url))
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def ensure_filtering_rules_for_instance(self, instance_ref):
|
|
||||||
""" Setting up inevitable filtering rules on compute node,
|
|
||||||
and waiting for its completion.
|
|
||||||
To migrate an instance, filtering rules to hypervisors
|
|
||||||
and firewalls are inevitable on destination host.
|
|
||||||
( Waiting only for filterling rules to hypervisor,
|
|
||||||
since filtering rules to firewall rules can be set faster).
|
|
||||||
|
|
||||||
Concretely, the below method must be called.
|
|
||||||
- setup_basic_filtering (for nova-basic, etc.)
|
|
||||||
- prepare_instance_filter(for nova-instance-instance-xxx, etc.)
|
|
||||||
|
|
||||||
to_xml may have to be called since it defines PROJNET, PROJMASK.
|
|
||||||
but libvirt migrates those value through migrateToURI(),
|
|
||||||
so , no need to be called.
|
|
||||||
|
|
||||||
Don't use thread for this method since migration should
|
|
||||||
not be started when setting-up filtering rules operations
|
|
||||||
are not completed."""
|
|
||||||
|
|
||||||
# Tf any instances never launch at destination host,
|
|
||||||
# basic-filtering must be set here.
|
|
||||||
self.nwfilter.setup_basic_filtering(instance_ref)
|
|
||||||
# setting up n)ova-instance-instance-xx mainly.
|
|
||||||
self.firewall_driver.prepare_instance_filter(instance_ref)
|
|
||||||
|
|
||||||
# wait for completion
|
|
||||||
timeout_count = range(FLAGS.live_migration_timeout_sec * 2)
|
|
||||||
while len(timeout_count) != 0:
|
|
||||||
try:
|
|
||||||
filter_name = 'nova-instance-%s' % instance_ref.name
|
|
||||||
self._conn.nwfilterLookupByName(filter_name)
|
|
||||||
break
|
|
||||||
except libvirt.libvirtError:
|
|
||||||
timeout_count.pop()
|
|
||||||
if len(timeout_count) == 0:
|
|
||||||
ec2_id = instance_ref['hostname']
|
|
||||||
msg = _('Timeout migrating for %s(%s)')
|
|
||||||
raise exception.Error(msg % (ec2_id, instance_ref.name))
|
|
||||||
time.sleep(0.5)
|
|
||||||
|
|
||||||
def live_migration(self, context, instance_ref, dest):
|
|
||||||
"""
|
|
||||||
Just spawning live_migration operation for
|
|
||||||
distributing high-load.
|
|
||||||
"""
|
|
||||||
greenthread.spawn(self._live_migration, context, instance_ref, dest)
|
|
||||||
|
|
||||||
def _live_migration(self, context, instance_ref, dest):
|
|
||||||
""" Do live migration."""
|
|
||||||
|
|
||||||
# Do live migration.
|
|
||||||
try:
|
|
||||||
duri = FLAGS.live_migration_uri % dest
|
|
||||||
|
|
||||||
flaglist = FLAGS.live_migration_flag.split(',')
|
|
||||||
flagvals = [getattr(libvirt, x.strip()) for x in flaglist]
|
|
||||||
logical_sum = reduce(lambda x, y: x | y, flagvals)
|
|
||||||
|
|
||||||
bandwidth = FLAGS.live_migration_bandwidth
|
|
||||||
|
|
||||||
if self.read_only:
|
|
||||||
tmpconn = self._connect(self.libvirt_uri, False)
|
|
||||||
dom = tmpconn.lookupByName(instance_ref.name)
|
|
||||||
dom.migrateToURI(duri, logical_sum, None, bandwidth)
|
|
||||||
tmpconn.close()
|
|
||||||
else:
|
|
||||||
dom = self._conn.lookupByName(instance_ref.name)
|
|
||||||
dom.migrateToURI(duri, logical_sum, None, bandwidth)
|
|
||||||
|
|
||||||
except Exception, e:
|
|
||||||
id = instance_ref['id']
|
|
||||||
db.instance_set_state(context, id, power_state.RUNNING, 'running')
|
|
||||||
for v in instance_ref['volumes']:
|
|
||||||
db.volume_update(context,
|
|
||||||
v['id'],
|
|
||||||
{'status': 'in-use'})
|
|
||||||
|
|
||||||
raise e
|
|
||||||
|
|
||||||
# Waiting for completion of live_migration.
|
|
||||||
timer = utils.LoopingCall(f=None)
|
|
||||||
|
|
||||||
def wait_for_live_migration():
|
|
||||||
|
|
||||||
try:
|
|
||||||
state = self.get_info(instance_ref.name)['state']
|
|
||||||
except exception.NotFound:
|
|
||||||
timer.stop()
|
|
||||||
self._post_live_migration(context, instance_ref, dest)
|
|
||||||
|
|
||||||
timer.f = wait_for_live_migration
|
|
||||||
timer.start(interval=0.5, now=True)
|
|
||||||
|
|
||||||
def _post_live_migration(self, context, instance_ref, dest):
|
|
||||||
"""
|
|
||||||
Post operations for live migration.
|
|
||||||
Mainly, database updating.
|
|
||||||
"""
|
|
||||||
LOG.info('post livemigration operation is started..')
|
|
||||||
# Detaching volumes.
|
|
||||||
# (not necessary in current version )
|
|
||||||
|
|
||||||
# Releasing vlan.
|
|
||||||
# (not necessary in current implementation?)
|
|
||||||
|
|
||||||
# Releasing security group ingress rule.
|
|
||||||
if FLAGS.firewall_driver == \
|
|
||||||
'nova.virt.libvirt_conn.IptablesFirewallDriver':
|
|
||||||
try:
|
|
||||||
self.firewall_driver.unfilter_instance(instance_ref)
|
|
||||||
except KeyError, e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Database updating.
|
|
||||||
ec2_id = instance_ref['hostname']
|
|
||||||
|
|
||||||
instance_id = instance_ref['id']
|
|
||||||
fixed_ip = db.instance_get_fixed_address(context, instance_id)
|
|
||||||
# Not return if fixed_ip is not found, otherwise,
|
|
||||||
# instance never be accessible..
|
|
||||||
if None == fixed_ip:
|
|
||||||
logging.warn('fixed_ip is not found for %s ' % ec2_id)
|
|
||||||
db.fixed_ip_update(context, fixed_ip, {'host': dest})
|
|
||||||
network_ref = db.fixed_ip_get_network(context, fixed_ip)
|
|
||||||
db.network_update(context, network_ref['id'], {'host': dest})
|
|
||||||
|
|
||||||
try:
|
|
||||||
floating_ip \
|
|
||||||
= db.instance_get_floating_address(context, instance_id)
|
|
||||||
# Not return if floating_ip is not found, otherwise,
|
|
||||||
# instance never be accessible..
|
|
||||||
if None == floating_ip:
|
|
||||||
logging.error('floating_ip is not found for %s ' % ec2_id)
|
|
||||||
else:
|
|
||||||
floating_ip_ref = db.floating_ip_get_by_address(context,
|
|
||||||
floating_ip)
|
|
||||||
db.floating_ip_update(context,
|
|
||||||
floating_ip_ref['address'],
|
|
||||||
{'host': dest})
|
|
||||||
except exception.NotFound:
|
|
||||||
logging.debug('%s doesnt have floating_ip.. ' % ec2_id)
|
|
||||||
except:
|
|
||||||
msg = 'Live migration: Unexpected error:'
|
|
||||||
msg += '%s cannot inherit floating ip.. ' % ec2_id
|
|
||||||
logging.error(_(msg))
|
|
||||||
|
|
||||||
# Restore instance/volume state
|
|
||||||
db.instance_update(context,
|
|
||||||
instance_id,
|
|
||||||
{'state_description': 'running',
|
|
||||||
'state': power_state.RUNNING,
|
|
||||||
'host': dest})
|
|
||||||
|
|
||||||
for v in instance_ref['volumes']:
|
|
||||||
db.volume_update(context,
|
|
||||||
v['id'],
|
|
||||||
{'status': 'in-use'})
|
|
||||||
|
|
||||||
logging.info(_('Live migrating %s to %s finishes successfully')
|
|
||||||
% (ec2_id, dest))
|
|
||||||
msg = _(("""Known error: the below error is nomally occurs.\n"""
|
|
||||||
"""Just check if iinstance is successfully migrated.\n"""
|
|
||||||
"""libvir: QEMU error : Domain not found: no domain """
|
|
||||||
"""with matching name.."""))
|
|
||||||
logging.info(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class FirewallDriver(object):
|
class FirewallDriver(object):
|
||||||
def prepare_instance_filter(self, instance):
|
def prepare_instance_filter(self, instance):
|
||||||
|
@ -209,36 +209,6 @@ class XenAPIConnection(object):
|
|||||||
'username': FLAGS.xenapi_connection_username,
|
'username': FLAGS.xenapi_connection_username,
|
||||||
'password': FLAGS.xenapi_connection_password}
|
'password': FLAGS.xenapi_connection_password}
|
||||||
|
|
||||||
def get_cpu_info(self):
|
|
||||||
"""This method is supported only libvirt. """
|
|
||||||
return
|
|
||||||
|
|
||||||
def get_vcpu_number(self):
|
|
||||||
"""This method is supported only libvirt. """
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def get_memory_mb(self):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def get_local_gb(self):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def get_hypervisor_type(self):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
return
|
|
||||||
|
|
||||||
def get_hypervisor_version(self):
|
|
||||||
"""This method is supported only libvirt.."""
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def compare_cpu(self, xml):
|
|
||||||
raise NotImplementedError('This method is supported only libvirt.')
|
|
||||||
|
|
||||||
def live_migration(self, context, instance_ref, dest):
|
|
||||||
raise NotImplementedError('This method is supported only libvirt.')
|
|
||||||
|
|
||||||
|
|
||||||
class XenAPISession(object):
|
class XenAPISession(object):
|
||||||
"""The session to invoke XenAPI SDK calls"""
|
"""The session to invoke XenAPI SDK calls"""
|
||||||
|
@ -122,7 +122,7 @@ class VolumeDriver(object):
|
|||||||
"""Removes an export for a logical volume."""
|
"""Removes an export for a logical volume."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def discover_volume(self, _context, volume):
|
def discover_volume(self, volume):
|
||||||
"""Discover volume on a remote host."""
|
"""Discover volume on a remote host."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -184,35 +184,15 @@ class AOEDriver(VolumeDriver):
|
|||||||
self._try_execute("sudo vblade-persist destroy %s %s" %
|
self._try_execute("sudo vblade-persist destroy %s %s" %
|
||||||
(shelf_id, blade_id))
|
(shelf_id, blade_id))
|
||||||
|
|
||||||
def discover_volume(self, context, volume):
|
def discover_volume(self, _volume):
|
||||||
"""Discover volume on a remote host."""
|
"""Discover volume on a remote host."""
|
||||||
self._execute("sudo aoe-discover")
|
self._execute("sudo aoe-discover")
|
||||||
self._execute("sudo aoe-stat", check_exit_code=False)
|
self._execute("sudo aoe-stat", check_exit_code=False)
|
||||||
shelf_id, blade_id = self.db.volume_get_shelf_and_blade(context,
|
|
||||||
volume['id'])
|
|
||||||
return "/dev/etherd/e%s.%s" % (shelf_id, blade_id)
|
|
||||||
|
|
||||||
def undiscover_volume(self, _volume):
|
def undiscover_volume(self, _volume):
|
||||||
"""Undiscover volume on a remote host."""
|
"""Undiscover volume on a remote host."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def check_for_export(self, context, volume_id):
|
|
||||||
"""Make sure whether volume is exported."""
|
|
||||||
(shelf_id,
|
|
||||||
blade_id) = self.db.volume_get_shelf_and_blade(context,
|
|
||||||
volume_id)
|
|
||||||
(out, _err) = self._execute("sudo vblade-persist ls --no-header")
|
|
||||||
exists = False
|
|
||||||
for line in out.split('\n'):
|
|
||||||
param = line.split(' ')
|
|
||||||
if len(param) == 6 and param[0] == str(shelf_id) \
|
|
||||||
and param[1] == str(blade_id) and param[-1] == "run":
|
|
||||||
exists = True
|
|
||||||
break
|
|
||||||
if not exists:
|
|
||||||
logging.warning(_("vblade process for e%s.%s isn't running.")
|
|
||||||
% (shelf_id, blade_id))
|
|
||||||
|
|
||||||
|
|
||||||
class FakeAOEDriver(AOEDriver):
|
class FakeAOEDriver(AOEDriver):
|
||||||
"""Logs calls instead of executing."""
|
"""Logs calls instead of executing."""
|
||||||
@ -296,7 +276,7 @@ class ISCSIDriver(VolumeDriver):
|
|||||||
iscsi_portal = location.split(",")[0]
|
iscsi_portal = location.split(",")[0]
|
||||||
return (iscsi_name, iscsi_portal)
|
return (iscsi_name, iscsi_portal)
|
||||||
|
|
||||||
def discover_volume(self, _context, volume):
|
def discover_volume(self, volume):
|
||||||
"""Discover volume on a remote host."""
|
"""Discover volume on a remote host."""
|
||||||
iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'],
|
iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'],
|
||||||
volume['host'])
|
volume['host'])
|
||||||
@ -384,7 +364,7 @@ class RBDDriver(VolumeDriver):
|
|||||||
"""Removes an export for a logical volume"""
|
"""Removes an export for a logical volume"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def discover_volume(self, _context, volume):
|
def discover_volume(self, volume):
|
||||||
"""Discover volume on a remote host"""
|
"""Discover volume on a remote host"""
|
||||||
return "rbd:%s/%s" % (FLAGS.rbd_pool, volume['name'])
|
return "rbd:%s/%s" % (FLAGS.rbd_pool, volume['name'])
|
||||||
|
|
||||||
@ -433,7 +413,7 @@ class SheepdogDriver(VolumeDriver):
|
|||||||
"""Removes an export for a logical volume"""
|
"""Removes an export for a logical volume"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def discover_volume(self, _context, volume):
|
def discover_volume(self, volume):
|
||||||
"""Discover volume on a remote host"""
|
"""Discover volume on a remote host"""
|
||||||
return "sheepdog:%s" % volume['name']
|
return "sheepdog:%s" % volume['name']
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ class VolumeManager(manager.Manager):
|
|||||||
if volume_ref['host'] == self.host and FLAGS.use_local_volumes:
|
if volume_ref['host'] == self.host and FLAGS.use_local_volumes:
|
||||||
path = self.driver.local_path(volume_ref)
|
path = self.driver.local_path(volume_ref)
|
||||||
else:
|
else:
|
||||||
path = self.driver.discover_volume(context, volume_ref)
|
path = self.driver.discover_volume(volume_ref)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def remove_compute_volume(self, context, volume_id):
|
def remove_compute_volume(self, context, volume_id):
|
||||||
@ -149,10 +149,3 @@ class VolumeManager(manager.Manager):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
self.driver.undiscover_volume(volume_ref)
|
self.driver.undiscover_volume(volume_ref)
|
||||||
|
|
||||||
def check_for_export(self, context, instance_id):
|
|
||||||
"""Make sure whether volume is exported."""
|
|
||||||
if FLAGS.volume_driver == 'nova.volume.driver.AOEDriver':
|
|
||||||
instance_ref = self.db.instance_get(instance_id)
|
|
||||||
for v in instance_ref['volumes']:
|
|
||||||
self.driver.check_for_export(context, v['id'])
|
|
||||||
|
Loading…
Reference in New Issue
Block a user