summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/nova-manage55
-rw-r--r--nova/volume/driver.py33
-rw-r--r--nova/volume/manager.py37
3 files changed, 112 insertions, 13 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index d0901ddfc..a347e86ce 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -79,7 +79,9 @@ from nova import exception
from nova import flags
from nova import log as logging
from nova import quota
+from nova import rpc
from nova import utils
+from nova.api.ec2.cloud import ec2_id_to_id
from nova.auth import manager
from nova.cloudpipe import pipelib
from nova.db import migration
@@ -95,6 +97,16 @@ flags.DECLARE('vpn_start', 'nova.network.manager')
flags.DECLARE('fixed_range_v6', 'nova.network.manager')
+def param2id(object_id):
+ """Helper function to convert various id types to internal id.
+ args: [object_id], e.g. 'vol-0000000a' or 'volume-0000000a' or '10'
+ """
+ if '-' in object_id:
+ return ec2_id_to_id(object_id)
+ else:
+ return int(object_id)
+
+
class VpnCommands(object):
"""Class for managing VPNs."""
@@ -535,6 +547,46 @@ class DbCommands(object):
print migration.db_version()
+class VolumeCommands(object):
+ """Methods for dealing with a cloud in an odd state"""
+
+ def delete(self, volume_id):
+ """Delete a volume, bypassing the check that it
+ must be available.
+ args: volume_id_id"""
+ ctxt = context.get_admin_context()
+ volume = db.volume_get(ctxt, param2id(volume_id))
+ host = volume['host']
+ if volume['status'] == 'in-use':
+ print "Volume is in-use."
+ print "Detach volume from instance and then try again."
+ return
+
+ rpc.cast(ctxt,
+ db.queue_get_for(ctxt, FLAGS.volume_topic, host),
+ {"method": "delete_volume",
+ "args": {"volume_id": volume['id']}})
+
+ def reattach(self, volume_id):
+ """Re-attach a volume that has previously been attached
+ to an instance. Typically called after a compute host
+ has been rebooted.
+ args: volume_id_id"""
+ ctxt = context.get_admin_context()
+ volume = db.volume_get(ctxt, param2id(volume_id))
+ if not volume['instance_id']:
+ print "volume is not attached to an instance"
+ return
+ instance = db.instance_get(ctxt, volume['instance_id'])
+ host = instance['host']
+ rpc.cast(ctxt,
+ db.queue_get_for(ctxt, FLAGS.compute_topic, host),
+ {"method": "attach_volume",
+ "args": {"instance_id": instance['id'],
+ "volume_id": volume['id'],
+ "mountpoint": volume['mountpoint']}})
+
+
CATEGORIES = [
('user', UserCommands),
('project', ProjectCommands),
@@ -545,7 +597,8 @@ CATEGORIES = [
('network', NetworkCommands),
('service', ServiceCommands),
('log', LogCommands),
- ('db', DbCommands)]
+ ('db', DbCommands),
+ ('volume', VolumeCommands)]
def lazy_match(name, key_value_tuples):
diff --git a/nova/volume/driver.py b/nova/volume/driver.py
index 5fefa10cf..da7307733 100644
--- a/nova/volume/driver.py
+++ b/nova/volume/driver.py
@@ -100,6 +100,14 @@ class VolumeDriver(object):
def delete_volume(self, volume):
"""Deletes a logical volume."""
+ try:
+ self._try_execute("sudo lvdisplay %s/%s" %
+ (FLAGS.volume_group,
+ volume['name']))
+ except Exception as e:
+ # If the volume isn't present, then don't attempt to delete
+ return True
+
self._try_execute("sudo lvremove -f %s/%s" %
(FLAGS.volume_group,
volume['name']))
@@ -218,8 +226,14 @@ class ISCSIDriver(VolumeDriver):
def ensure_export(self, context, volume):
"""Synchronously recreates an export for a logical volume."""
- iscsi_target = self.db.volume_get_iscsi_target_num(context,
+ try:
+ iscsi_target = self.db.volume_get_iscsi_target_num(context,
volume['id'])
+ except exception.NotFound:
+ LOG.info(_("Skipping ensure_export. No iscsi_target " +
+ "provisioned for volume: %d"), volume['id'])
+ return
+
iscsi_name = "%s%s" % (FLAGS.iscsi_target_prefix, volume['name'])
volume_path = "/dev/%s/%s" % (FLAGS.volume_group, volume['name'])
self._sync_exec("sudo ietadm --op new "
@@ -258,8 +272,23 @@ class ISCSIDriver(VolumeDriver):
def remove_export(self, context, volume):
"""Removes an export for a logical volume."""
- iscsi_target = self.db.volume_get_iscsi_target_num(context,
+ try:
+ iscsi_target = self.db.volume_get_iscsi_target_num(context,
volume['id'])
+ except exception.NotFound:
+ LOG.info(_("Skipping remove_export. No iscsi_target " +
+ "provisioned for volume: %d"), volume['id'])
+ return
+
+ try:
+ # ietadm show will exit with an error
+ # this export has already been removed
+ self._execute("sudo ietadm --op show --tid=%s " % iscsi_target)
+ except Exception as e:
+ LOG.info(_("Skipping remove_export. No iscsi_target " +
+ "is presently exported for volume: %d"), volume['id'])
+ return
+
self._execute("sudo ietadm --op delete --tid=%s "
"--lun=0" % iscsi_target)
self._execute("sudo ietadm --op delete --tid=%s" %
diff --git a/nova/volume/manager.py b/nova/volume/manager.py
index 6348539c5..82e3521a8 100644
--- a/nova/volume/manager.py
+++ b/nova/volume/manager.py
@@ -84,7 +84,10 @@ class VolumeManager(manager.Manager):
volumes = self.db.volume_get_all_by_host(ctxt, self.host)
LOG.debug(_("Re-exporting %s volumes"), len(volumes))
for volume in volumes:
- self.driver.ensure_export(ctxt, volume)
+ if volume['status'] in ['available', 'in-use']:
+ self.driver.ensure_export(ctxt, volume)
+ else:
+ LOG.info(_("volume %s: skipping export"), volume_ref['name'])
def create_volume(self, context, volume_id):
"""Creates and exports the volume."""
@@ -99,12 +102,18 @@ class VolumeManager(manager.Manager):
# before passing it to the driver.
volume_ref['host'] = self.host
- LOG.debug(_("volume %s: creating lv of size %sG"), volume_ref['name'],
- volume_ref['size'])
- self.driver.create_volume(volume_ref)
+ try:
+ LOG.debug(_("volume %s: creating lv of size %sG"),
+ volume_ref['name'],
+ volume_ref['size'])
+ self.driver.create_volume(volume_ref)
- LOG.debug(_("volume %s: creating export"), volume_ref['name'])
- self.driver.create_export(context, volume_ref)
+ LOG.debug(_("volume %s: creating export"), volume_ref['name'])
+ self.driver.create_export(context, volume_ref)
+ except Exception as e:
+ self.db.volume_update(context,
+ volume_ref['id'], {'status': 'error'})
+ raise e
now = datetime.datetime.utcnow()
self.db.volume_update(context,
@@ -121,10 +130,18 @@ class VolumeManager(manager.Manager):
raise exception.Error(_("Volume is still attached"))
if volume_ref['host'] != self.host:
raise exception.Error(_("Volume is not local to this node"))
- LOG.debug(_("volume %s: removing export"), volume_ref['name'])
- self.driver.remove_export(context, volume_ref)
- LOG.debug(_("volume %s: deleting"), volume_ref['name'])
- self.driver.delete_volume(volume_ref)
+
+ try:
+ LOG.debug(_("volume %s: removing export"), volume_ref['name'])
+ self.driver.remove_export(context, volume_ref)
+ LOG.debug(_("volume %s: deleting"), volume_ref['name'])
+ self.driver.delete_volume(volume_ref)
+ except Exception as e:
+ self.db.volume_update(context,
+ volume_ref['id'],
+ {'status': 'error_deleting'})
+ raise e
+
self.db.volume_destroy(context, volume_id)
LOG.debug(_("volume %s: deleted successfully"), volume_ref['name'])
return True