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:
parent
db6c58b009
commit
61d88463da
@ -188,7 +188,10 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
|
|||||||
'reset_state': ['state'],
|
'reset_state': ['state'],
|
||||||
'create_image': ['name', 'metadata'],
|
'create_image': ['name', 'metadata'],
|
||||||
'migrate_live': ['host', 'block_migration', 'disk_over_commit'],
|
'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
|
assert len(body.keys()) == 1
|
||||||
action = list(body)[0]
|
action = list(body)[0]
|
||||||
|
47
novaclient/tests/v3/test_volumes.py
Normal file
47
novaclient/tests/v3/test_volumes.py
Normal 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')
|
@ -30,6 +30,7 @@ from novaclient.v3 import quotas
|
|||||||
from novaclient.v3 import servers
|
from novaclient.v3 import servers
|
||||||
from novaclient.v3 import services
|
from novaclient.v3 import services
|
||||||
from novaclient.v3 import usage
|
from novaclient.v3 import usage
|
||||||
|
from novaclient.v3 import volumes
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
@ -80,6 +81,7 @@ class Client(object):
|
|||||||
self.servers = servers.ServerManager(self)
|
self.servers = servers.ServerManager(self)
|
||||||
self.services = services.ServiceManager(self)
|
self.services = services.ServiceManager(self)
|
||||||
self.usage = usage.UsageManager(self)
|
self.usage = usage.UsageManager(self)
|
||||||
|
self.volumes = volumes.VolumeManager(self)
|
||||||
|
|
||||||
# Add in any extensions...
|
# Add in any extensions...
|
||||||
if extensions:
|
if extensions:
|
||||||
|
@ -1547,10 +1547,25 @@ def do_volume_attach(cs, args):
|
|||||||
if args.device == 'auto':
|
if args.device == 'auto':
|
||||||
args.device = None
|
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.volume,
|
||||||
args.device)
|
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',
|
@utils.arg('server',
|
||||||
|
68
novaclient/v3/volumes.py
Normal file
68
novaclient/v3/volumes.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user