Adds volume support for the V3 API

Adds the ability to attach, detach and swap volumes on
servers. There is no code shared with the v1_1 version
because for V3 the volumes interface is completely different
and the attach/detach/swap functionality is simply a server
action rather than something accessed through a special resource.

Partially implements blueprint v3-api

Change-Id: Ib405f821fe557745d11cff9db08381fc15233fe5
This commit is contained in:
Chris Yeoh 2013-12-24 00:28:13 +10:30 committed by Christopher Yeoh
parent db6c58b009
commit 61d88463da
5 changed files with 138 additions and 3 deletions

View File

@ -188,7 +188,10 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
'reset_state': ['state'],
'create_image': ['name', 'metadata'],
'migrate_live': ['host', 'block_migration', 'disk_over_commit'],
'create_backup': ['name', 'backup_type', 'rotation']}
'create_backup': ['name', 'backup_type', 'rotation'],
'attach': ['volume_id', 'device'],
'detach': ['volume_id'],
'swap_volume_attachment': ['old_volume_id', 'new_volume_id']}
assert len(body.keys()) == 1
action = list(body)[0]

View File

@ -0,0 +1,47 @@
# Copyright 2013 IBM Corp.
# All Rights Reserved.
#
# 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 novaclient.tests import utils
from novaclient.tests.v3 import fakes
class VolumesTest(utils.TestCase):
def setUp(self):
super(VolumesTest, self).setUp()
self.cs = self._get_fake_client()
def _get_fake_client(self):
return fakes.FakeClient()
def test_attach_server_volume(self):
v = self.cs.volumes.attach_server_volume(
server=1234,
volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983',
device='/dev/vdb'
)
self.cs.assert_called('POST', '/servers/1234/action')
def test_update_server_volume(self):
vol_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983'
v = self.cs.volumes.update_server_volume(
server=1234,
old_volume_id='Work',
new_volume_id=vol_id
)
self.cs.assert_called('POST', '/servers/1234/action')
def test_delete_server_volume(self):
self.cs.volumes.delete_server_volume(1234, 'Work')
self.cs.assert_called('POST', '/servers/1234/action')

View File

@ -30,6 +30,7 @@ from novaclient.v3 import quotas
from novaclient.v3 import servers
from novaclient.v3 import services
from novaclient.v3 import usage
from novaclient.v3 import volumes
class Client(object):
@ -80,6 +81,7 @@ class Client(object):
self.servers = servers.ServerManager(self)
self.services = services.ServiceManager(self)
self.usage = usage.UsageManager(self)
self.volumes = volumes.VolumeManager(self)
# Add in any extensions...
if extensions:

View File

@ -1547,10 +1547,25 @@ def do_volume_attach(cs, args):
if args.device == 'auto':
args.device = None
volume = cs.volumes.create_server_volume(_find_server(cs, args.server).id,
volume = cs.volumes.attach_server_volume(_find_server(cs, args.server).id,
args.volume,
args.device)
_print_volume(volume)
@utils.arg('server',
metavar='<server>',
help='Name or ID of server.')
@utils.arg('attachment_id',
metavar='<volume>',
help='Attachment ID of the volume.')
@utils.arg('new_volume',
metavar='<volume>',
help='ID of the volume to attach.')
def do_volume_update(cs, args):
"""Update volume attachment."""
volume = cs.volumes.update_server_volume(_find_server(cs, args.server).id,
args.attachment_id,
args.new_volume)
@utils.arg('server',

68
novaclient/v3/volumes.py Normal file
View File

@ -0,0 +1,68 @@
# Copyright 2013 IBM Corp.
#
# 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.
"""
Volume interface
"""
from novaclient import base
class VolumeManager(base.Manager):
"""
Manage :class:`Volume` resources.
"""
def attach_server_volume(self, server, volume_id, device):
"""
Attach a volume identified by the volume ID to the given server ID
:param server: The server (or it's ID)
:param volume_id: The ID of the volume to attach.
:param device: The device name
:rtype: :class:`Volume`
"""
body = {'volume_id': volume_id, 'device': device}
return self._action('attach', server, body)
def update_server_volume(self, server, old_volume_id, new_volume_id):
"""
Update the volume identified by the attachment ID, that is attached to
the given server ID
:param server_id: The server (or it's ID)
:param old_volume_id: The ID of the attachment
:param new_volume_id: The ID of the new volume to attach
:rtype: :class:`Volume`
"""
body = {'new_volume_id': new_volume_id, 'old_volume_id': old_volume_id}
return self._action('swap_volume_attachment', server, body)
def delete_server_volume(self, server, volume_id):
"""
Detach a volume identified by the attachment ID from the given server
:param server_id: The ID of the server
:param volume_id: The ID of the attachment
"""
return self._action('detach', server, {'volume_id': volume_id})
def _action(self, action, server, info=None, **kwargs):
"""
Perform a server "action" -- reboot/rebuild/resize/etc.
"""
body = {action: info}
self.run_hooks('modify_body_for_action', body, **kwargs)
url = '/servers/%s/action' % base.getid(server)
return self.api.client.post(url, body=body)