1# Copyright 2011 Denali Systems, Inc. 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16""" 17Volume interface 18""" 19import warnings 20 21from novaclient import api_versions 22from novaclient import base 23 24 25class Volume(base.Resource): 26 """ 27 A volume is an extra block level storage to the OpenStack 28 instances. 29 """ 30 NAME_ATTR = 'display_name' 31 32 def __repr__(self): 33 return "<Volume: %s>" % self.id 34 35 36class VolumeManager(base.Manager): 37 """ 38 Manage :class:`Volume` resources. This is really about volume attachments. 39 """ 40 resource_class = Volume 41 42 @api_versions.wraps("2.0", "2.48") 43 def create_server_volume(self, server_id, volume_id, device=None): 44 """ 45 Attach a volume identified by the volume ID to the given server ID 46 47 :param server_id: The ID of the server 48 :param volume_id: The ID of the volume to attach. 49 :param device: The device name (optional) 50 :rtype: :class:`Volume` 51 """ 52 body = {'volumeAttachment': {'volumeId': volume_id}} 53 if device is not None: 54 body['volumeAttachment']['device'] = device 55 return self._create("/servers/%s/os-volume_attachments" % server_id, 56 body, "volumeAttachment") 57 58 @api_versions.wraps("2.49", "2.78") 59 def create_server_volume(self, server_id, volume_id, device=None, 60 tag=None): 61 """ 62 Attach a volume identified by the volume ID to the given server ID 63 64 :param server_id: The ID of the server 65 :param volume_id: The ID of the volume to attach. 66 :param device: The device name (optional) 67 :param tag: The tag (optional) 68 :rtype: :class:`Volume` 69 """ 70 body = {'volumeAttachment': {'volumeId': volume_id}} 71 if device is not None: 72 body['volumeAttachment']['device'] = device 73 if tag is not None: 74 body['volumeAttachment']['tag'] = tag 75 return self._create("/servers/%s/os-volume_attachments" % server_id, 76 body, "volumeAttachment") 77 78 @api_versions.wraps("2.79") 79 def create_server_volume(self, server_id, volume_id, device=None, 80 tag=None, delete_on_termination=False): 81 """ 82 Attach a volume identified by the volume ID to the given server ID 83 84 :param server_id: The ID of the server. 85 :param volume_id: The ID of the volume to attach. 86 :param device: The device name (optional). 87 :param tag: The tag (optional). 88 :param delete_on_termination: Marked whether to delete the attached 89 volume when the server is deleted 90 (optional). 91 :rtype: :class:`Volume` 92 """ 93 # TODO(mriedem): Move this body construction into a private common 94 # helper method for all versions of create_server_volume to use. 95 body = {'volumeAttachment': {'volumeId': volume_id}} 96 if device is not None: 97 body['volumeAttachment']['device'] = device 98 if tag is not None: 99 body['volumeAttachment']['tag'] = tag 100 if delete_on_termination: 101 body['volumeAttachment']['delete_on_termination'] = ( 102 delete_on_termination) 103 return self._create("/servers/%s/os-volume_attachments" % server_id, 104 body, "volumeAttachment") 105 106 @api_versions.wraps("2.0", "2.84") 107 def update_server_volume(self, server_id, src_volid, dest_volid): 108 """ 109 Swaps the existing volume attachment to point to a new volume. 110 111 Takes a server, a source (attached) volume and a destination volume and 112 performs a hypervisor assisted data migration from src to dest volume, 113 detaches the original (source) volume and attaches the new destination 114 volume. Note that not all backing hypervisor drivers support this 115 operation and it may be disabled via policy. 116 117 118 :param server_id: The ID of the server 119 :param source_volume: The ID of the src volume 120 :param dest_volume: The ID of the destination volume 121 :rtype: :class:`Volume` 122 """ 123 body = {'volumeAttachment': {'volumeId': dest_volid}} 124 return self._update("/servers/%s/os-volume_attachments/%s" % 125 (server_id, src_volid,), 126 body, "volumeAttachment") 127 128 @api_versions.wraps("2.85") 129 def update_server_volume(self, server_id, src_volid, dest_volid, 130 delete_on_termination=None): 131 """ 132 Swaps the existing volume attachment to point to a new volume. 133 134 Takes a server, a source (attached) volume and a destination volume and 135 performs a hypervisor assisted data migration from src to dest volume, 136 detaches the original (source) volume and attaches the new destination 137 volume. Note that not all backing hypervisor drivers support this 138 operation and it may be disabled via policy. 139 140 141 :param server_id: The ID of the server 142 :param source_volume: The ID of the src volume 143 :param dest_volume: The ID of the destination volume 144 :param delete_on_termination: Marked whether to delete the attached 145 volume when the server is deleted 146 (optional). 147 :rtype: :class:`Volume` 148 """ 149 body = {'volumeAttachment': {'volumeId': dest_volid}} 150 if delete_on_termination is not None: 151 body['volumeAttachment']['delete_on_termination'] = ( 152 delete_on_termination) 153 return self._update("/servers/%s/os-volume_attachments/%s" % 154 (server_id, src_volid), 155 body, "volumeAttachment") 156 157 def get_server_volume(self, server_id, volume_id=None, attachment_id=None): 158 """ 159 Get the volume identified by the volume ID, that is attached to 160 the given server ID 161 162 :param server_id: The ID of the server 163 :param volume_id: The ID of the volume to attach 164 :rtype: :class:`Volume` 165 """ 166 167 if attachment_id is not None and volume_id is not None: 168 raise TypeError("You cannot specify both volume_id " 169 "and attachment_id arguments.") 170 171 elif attachment_id is not None: 172 warnings.warn("attachment_id argument " 173 "of volumes.get_server_volume " 174 "method is deprecated in favor " 175 "of volume_id.") 176 volume_id = attachment_id 177 178 if volume_id is None: 179 raise TypeError("volume_id is required argument.") 180 181 return self._get("/servers/%s/os-volume_attachments/%s" % (server_id, 182 volume_id,), "volumeAttachment") 183 184 def get_server_volumes(self, server_id): 185 """ 186 Get a list of all the attached volumes for the given server ID 187 188 :param server_id: The ID of the server 189 :rtype: list of :class:`Volume` 190 """ 191 return self._list("/servers/%s/os-volume_attachments" % server_id, 192 "volumeAttachments") 193 194 def delete_server_volume(self, server_id, volume_id=None, 195 attachment_id=None): 196 """ 197 Detach a volume identified by the volume ID from the given server 198 199 :param server_id: The ID of the server 200 :param volume_id: The ID of the volume to attach 201 :returns: An instance of novaclient.base.TupleWithMeta 202 """ 203 if attachment_id is not None and volume_id is not None: 204 raise TypeError("You cannot specify both volume_id " 205 "and attachment_id arguments.") 206 207 elif attachment_id is not None: 208 warnings.warn("attachment_id argument " 209 "of volumes.delete_server_volume " 210 "method is deprecated in favor " 211 "of volume_id.") 212 volume_id = attachment_id 213 214 if volume_id is None: 215 raise TypeError("volume_id is required argument.") 216 217 return self._delete("/servers/%s/os-volume_attachments/%s" % 218 (server_id, volume_id,)) 219