Commit cf006949 authored by Stavros Sachtouris's avatar Stavros Sachtouris

Implement "wait" functionality for volumes

Fixes grnet/kamaki#78
parent b26ee692
......@@ -480,7 +480,8 @@ class StatusArgument(ValueArgument):
First state is the default"""
def __init__(self, *args, **kwargs):
self.valid_states = kwargs.pop('valid_states', ['BUILD', ])
self.valid_states = [
s.upper() for s in kwargs.pop('valid_states', ['BUILD', ])]
super(StatusArgument, self).__init__(*args, **kwargs)
@property
......
......@@ -32,10 +32,11 @@
# or implied, of GRNET S.A.command
from kamaki.cli import command
from kamaki.cli.errors import CLIInvalidArgument
from kamaki.cli.cmdtree import CommandTree
from kamaki.cli.cmds import (
CommandInit, errors, client_log, OptionalOutput)
from kamaki.clients.cyclades import CycladesBlockStorageClient
CommandInit, errors, client_log, OptionalOutput, Wait)
from kamaki.clients.cyclades import CycladesBlockStorageClient, ClientError
from kamaki.cli import argument
......@@ -44,6 +45,24 @@ snapshot_cmds = CommandTree('snapshot', 'Block Storage API snapshot commands')
namespaces = [volume_cmds, snapshot_cmds]
_commands = namespaces
volume_states = (
'creating', 'available', 'attaching', 'detaching', 'in_use', 'deleting',
'deleted', 'error', 'error_deleting', 'backing_up', 'restoring_backup',
'error_restoring', )
class _VolumeWait(Wait):
def wait_while(self, volume_id, current_status, timeout=60):
super(_VolumeWait, self).wait(
'Volume', volume_id, self.client.wait_volume_while, current_status,
timeout=timeout)
def wait_until(self, volume_id, target_status, timeout=60):
super(_VolumeWait, self).wait(
'Volume', volume_id, self.client.wait_volume_until, target_status,
timeout=timeout, msg='not yet')
class _BlockStorageInit(CommandInit):
@errors.Generic.all
......@@ -88,7 +107,7 @@ class volume_info(_BlockStorageInit, OptionalOutput):
@command(volume_cmds)
class volume_create(_BlockStorageInit, OptionalOutput):
class volume_create(_BlockStorageInit, OptionalOutput, _VolumeWait):
"""Create a new volume"""
arguments = dict(
......@@ -110,22 +129,29 @@ class volume_create(_BlockStorageInit, OptionalOutput):
'Metadata of key=value form (can be repeated)', '--metadata'),
project_id=argument.ValueArgument(
'Assign volume to a project', '--project-id'),
wait=argument.FlagArgument(
'Wait volume to be created and ready for use', ('-w', '--wait')),
)
required = ('size', 'server_id', 'name')
@errors.Generic.all
def _run(self, size, server_id, name):
self.print_(
self.client.create_volume(
size, server_id, name,
# source_volid=self['src_volume_id'],
display_description=self['description'],
snapshot_id=self['snapshot_id'],
imageRef=self['image_id'],
volume_type=self['volume_type'],
metadata=self['metadata'],
project=self['project_id']),
self.print_dict)
r = self.client.create_volume(
size, server_id, name,
# source_volid=self['src_volume_id'],
display_description=self['description'],
snapshot_id=self['snapshot_id'],
imageRef=self['image_id'],
volume_type=self['volume_type'],
metadata=self['metadata'],
project=self['project_id'])
self.print_dict(r)
r = self.client.get_volume_details(r['id'])
if self['wait'] and r['status'] != 'in_use':
self.wait_while(r['id'], r['status'])
r = self.client.get_volume_details(r['id'])
if r['status'] != 'in_use':
exit(1)
def main(self):
super(self.__class__, self)._run()
......@@ -178,15 +204,74 @@ class volume_reassign(_BlockStorageInit):
@command(volume_cmds)
class volume_delete(_BlockStorageInit):
class volume_delete(_BlockStorageInit, _VolumeWait):
"""Delete a volume"""
arguments = dict(
wait=argument.FlagArgument('Wait until deleted', ('-w', '--wait')),
)
@errors.Generic.all
def _run(self, volume_id):
self.client.delete_volume(volume_id)
if self['wait']:
try:
r = self.client.get_volume_details(volume_id)
self.wait_while(volume_id, r['status'])
r = self.client.get_volume_details(volume_id)
if r['status'] != 'deleted':
exit(1)
except ClientError as ce:
if ce.status not in (404, ):
raise
def main(self, volume_id):
super(self.__class__, self)._run()
self._run(volume_id=volume_id)
@command(volume_cmds)
class volume_wait(_BlockStorageInit, _VolumeWait):
"""Wait for volume to finish (default: --while creating)"""
arguments = dict(
timeout=argument.IntArgument(
'Wait limit in seconds (default: 60)', '--timeout', default=60),
status_w=argument.StatusArgument(
'Wait while in status (%s)' % ','.join(volume_states), '--while',
valid_states=volume_states),
status_u=argument.StatusArgument(
'Wait until status is reached (%s)' % ','.join(volume_states),
'--until',
valid_states=volume_states),
)
@errors.Generic.all
def _run(self, volume_id):
r = self.client.get_volume_details(volume_id)
if self['status_u']:
if r['status'] == self['status_u'].lower():
self.error('Volume %s: already in %s' % (
volume_id, r['status']))
else:
self.wait_until(
volume_id, self['status_u'].lower(),
timeout=self['timeout'])
else:
status_w = self['status_w'].lower() or 'creating'
if r['status'] == status_w.lower():
self.wait_while(volume_id, status_w, timeout=self['timeout'])
else:
self.error('Volume %s status: %s' % (volume_id, r['status']))
def main(self, volume_id):
super(self.__class__, self)._run()
if all([self['status_w'], self['status_u']]):
raise CLIInvalidArgument(
'Invalid argument combination', importance=2, details=[
'Arguments %s and %s are mutually exclusive' % (
self.arguments['status_w'].lvalue,
self.arguments['status_u'].lvalue)])
self._run(volume_id=volume_id)
......
......@@ -60,12 +60,12 @@ class _PortWait(Wait):
def wait_while(self, port_id, current_status, timeout=60):
super(_PortWait, self).wait(
'Port', port_id, self.client.wait_port, current_status,
'Port', port_id, self.client.wait_port_while, current_status,
timeout=timeout)
def wait_until(self, port_id, current_status, timeout=60):
def wait_until(self, port_id, target_status, timeout=60):
super(_PortWait, self).wait(
'Port', port_id, self.client.wait_port, current_status,
'Port', port_id, self.client.wait_port_until, target_status,
timeout=timeout, msg='not yet')
......
......@@ -32,9 +32,10 @@
# or implied, of GRNET S.A.
from kamaki.clients.blockstorage.rest_api import BlockStorageRestClient
from kamaki.clients import ClientError, Waiter
class BlockStorageClient(BlockStorageRestClient):
class BlockStorageClient(BlockStorageRestClient, Waiter):
"""OpenStack Block Storage v2 client"""
def list_volumes(self, detail=None):
......@@ -137,3 +138,23 @@ class BlockStorageClient(BlockStorageRestClient):
def get_volume_type_details(self, type_id):
r = self.types_get(type_id)
return r.json['volume_type']
# Wait methods
def get_volume_status(self, volume_id):
r = self.get_volume_details(volume_id)
return r['status'], None
def wait_volume_while(
self, volume_id,
current_status='creating', delay=1, max_wait=100, wait_cb=None):
return self.wait_while(
volume_id, current_status, BlockStorageClient.get_volume_status,
delay, max_wait, wait_cb)
def wait_volume_until(
self, volume_id,
target_status='in_use', delay=1, max_wait=100, wait_cb=None):
return self.wait_until(
volume_id, target_status, BlockStorageClient.get_volume_status,
delay, max_wait, wait_cb)
__version__ = "0.13.1next"
__version_vcs_info__ = {
'branch': 'develop',
'revid': '94fc396',
'revno': 2486}
__version_user_email__ = "skalkoto@grnet.gr"
__version_user_name__ = "Nikos Skalkotos"
'branch': 'feature-wait',
'revid': '68cc68c',
'revno': 2492}
__version_user_email__ = "saxtouri@admin.grnet.gr"
__version_user_name__ = "Stavros Sachtouris"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment