1# 2# Licensed under the Apache License, Version 2.0 (the "License"); you may 3# not use this file except in compliance with the License. You may obtain 4# a copy of the License at 5# 6# http://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 10# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 11# License for the specific language governing permissions and limitations 12# under the License. 13# 14 15"""Volume v2 consistency group action implementations""" 16 17import logging 18 19from osc_lib.cli import format_columns 20from osc_lib.command import command 21from osc_lib import exceptions 22from osc_lib import utils 23 24from openstackclient.i18n import _ 25 26 27LOG = logging.getLogger(__name__) 28 29 30def _find_volumes(parsed_args_volumes, volume_client): 31 result = 0 32 uuid = '' 33 for volume in parsed_args_volumes: 34 try: 35 volume_id = utils.find_resource( 36 volume_client.volumes, volume).id 37 uuid += volume_id + ',' 38 except Exception as e: 39 result += 1 40 LOG.error(_("Failed to find volume with " 41 "name or ID '%(volume)s':%(e)s") 42 % {'volume': volume, 'e': e}) 43 44 return result, uuid 45 46 47class AddVolumeToConsistencyGroup(command.Command): 48 _description = _("Add volume(s) to consistency group") 49 50 def get_parser(self, prog_name): 51 parser = super(AddVolumeToConsistencyGroup, self).get_parser(prog_name) 52 parser.add_argument( 53 'consistency_group', 54 metavar="<consistency-group>", 55 help=_('Consistency group to contain <volume> (name or ID)'), 56 ) 57 parser.add_argument( 58 'volumes', 59 metavar='<volume>', 60 nargs='+', 61 help=_('Volume(s) to add to <consistency-group> (name or ID) ' 62 '(repeat option to add multiple volumes)'), 63 ) 64 return parser 65 66 def take_action(self, parsed_args): 67 volume_client = self.app.client_manager.volume 68 result, add_uuid = _find_volumes(parsed_args.volumes, volume_client) 69 70 if result > 0: 71 total = len(parsed_args.volumes) 72 LOG.error(_("%(result)s of %(total)s volumes failed " 73 "to add.") % {'result': result, 'total': total}) 74 75 if add_uuid: 76 add_uuid = add_uuid.rstrip(',') 77 consistency_group_id = utils.find_resource( 78 volume_client.consistencygroups, 79 parsed_args.consistency_group).id 80 volume_client.consistencygroups.update( 81 consistency_group_id, add_volumes=add_uuid) 82 83 84class CreateConsistencyGroup(command.ShowOne): 85 _description = _("Create new consistency group.") 86 87 def get_parser(self, prog_name): 88 parser = super(CreateConsistencyGroup, self).get_parser(prog_name) 89 parser.add_argument( 90 "name", 91 metavar="<name>", 92 nargs="?", 93 help=_("Name of new consistency group (default to None)") 94 ) 95 exclusive_group = parser.add_mutually_exclusive_group(required=True) 96 exclusive_group.add_argument( 97 "--volume-type", 98 metavar="<volume-type>", 99 help=_("Volume type of this consistency group (name or ID)") 100 ) 101 exclusive_group.add_argument( 102 "--consistency-group-source", 103 metavar="<consistency-group>", 104 help=_("Existing consistency group (name or ID)") 105 ) 106 exclusive_group.add_argument( 107 "--consistency-group-snapshot", 108 metavar="<consistency-group-snapshot>", 109 help=_("Existing consistency group snapshot (name or ID)") 110 ) 111 parser.add_argument( 112 "--description", 113 metavar="<description>", 114 help=_("Description of this consistency group") 115 ) 116 parser.add_argument( 117 "--availability-zone", 118 metavar="<availability-zone>", 119 help=_("Availability zone for this consistency group " 120 "(not available if creating consistency group " 121 "from source)"), 122 ) 123 return parser 124 125 def take_action(self, parsed_args): 126 volume_client = self.app.client_manager.volume 127 if parsed_args.volume_type: 128 volume_type_id = utils.find_resource( 129 volume_client.volume_types, 130 parsed_args.volume_type).id 131 consistency_group = volume_client.consistencygroups.create( 132 volume_type_id, 133 name=parsed_args.name, 134 description=parsed_args.description, 135 availability_zone=parsed_args.availability_zone 136 ) 137 else: 138 if parsed_args.availability_zone: 139 msg = _("'--availability-zone' option will not work " 140 "if creating consistency group from source") 141 LOG.warning(msg) 142 143 consistency_group_id = None 144 consistency_group_snapshot = None 145 if parsed_args.consistency_group_source: 146 consistency_group_id = utils.find_resource( 147 volume_client.consistencygroups, 148 parsed_args.consistency_group_source).id 149 elif parsed_args.consistency_group_snapshot: 150 consistency_group_snapshot = utils.find_resource( 151 volume_client.cgsnapshots, 152 parsed_args.consistency_group_snapshot).id 153 154 consistency_group = ( 155 volume_client.consistencygroups.create_from_src( 156 consistency_group_snapshot, 157 consistency_group_id, 158 name=parsed_args.name, 159 description=parsed_args.description 160 ) 161 ) 162 163 return zip(*sorted(consistency_group._info.items())) 164 165 166class DeleteConsistencyGroup(command.Command): 167 _description = _("Delete consistency group(s).") 168 169 def get_parser(self, prog_name): 170 parser = super(DeleteConsistencyGroup, self).get_parser(prog_name) 171 parser.add_argument( 172 'consistency_groups', 173 metavar='<consistency-group>', 174 nargs="+", 175 help=_('Consistency group(s) to delete (name or ID)'), 176 ) 177 parser.add_argument( 178 '--force', 179 action='store_true', 180 default=False, 181 help=_("Allow delete in state other than error or available"), 182 ) 183 return parser 184 185 def take_action(self, parsed_args): 186 volume_client = self.app.client_manager.volume 187 result = 0 188 189 for i in parsed_args.consistency_groups: 190 try: 191 consistency_group_id = utils.find_resource( 192 volume_client.consistencygroups, i).id 193 volume_client.consistencygroups.delete( 194 consistency_group_id, parsed_args.force) 195 except Exception as e: 196 result += 1 197 LOG.error(_("Failed to delete consistency group with " 198 "name or ID '%(consistency_group)s':%(e)s") 199 % {'consistency_group': i, 'e': e}) 200 201 if result > 0: 202 total = len(parsed_args.consistency_groups) 203 msg = (_("%(result)s of %(total)s consistency groups failed " 204 "to delete.") % {'result': result, 'total': total}) 205 raise exceptions.CommandError(msg) 206 207 208class ListConsistencyGroup(command.Lister): 209 _description = _("List consistency groups.") 210 211 def get_parser(self, prog_name): 212 parser = super(ListConsistencyGroup, self).get_parser(prog_name) 213 parser.add_argument( 214 '--all-projects', 215 action="store_true", 216 help=_('Show details for all projects. Admin only. ' 217 '(defaults to False)') 218 ) 219 parser.add_argument( 220 '--long', 221 action="store_true", 222 help=_('List additional fields in output') 223 ) 224 return parser 225 226 def take_action(self, parsed_args): 227 if parsed_args.long: 228 columns = ['ID', 'Status', 'Availability Zone', 229 'Name', 'Description', 'Volume Types'] 230 else: 231 columns = ['ID', 'Status', 'Name'] 232 volume_client = self.app.client_manager.volume 233 consistency_groups = volume_client.consistencygroups.list( 234 detailed=True, 235 search_opts={'all_tenants': parsed_args.all_projects} 236 ) 237 238 return (columns, ( 239 utils.get_item_properties( 240 s, columns, 241 formatters={'Volume Types': format_columns.ListColumn}) 242 for s in consistency_groups)) 243 244 245class RemoveVolumeFromConsistencyGroup(command.Command): 246 _description = _("Remove volume(s) from consistency group") 247 248 def get_parser(self, prog_name): 249 parser = \ 250 super(RemoveVolumeFromConsistencyGroup, self).get_parser(prog_name) 251 parser.add_argument( 252 'consistency_group', 253 metavar="<consistency-group>", 254 help=_('Consistency group containing <volume> (name or ID)'), 255 ) 256 parser.add_argument( 257 'volumes', 258 metavar='<volume>', 259 nargs='+', 260 help=_('Volume(s) to remove from <consistency-group> (name or ID) ' 261 '(repeat option to remove multiple volumes)'), 262 ) 263 return parser 264 265 def take_action(self, parsed_args): 266 volume_client = self.app.client_manager.volume 267 result, remove_uuid = _find_volumes(parsed_args.volumes, volume_client) 268 269 if result > 0: 270 total = len(parsed_args.volumes) 271 LOG.error(_("%(result)s of %(total)s volumes failed " 272 "to remove.") % {'result': result, 'total': total}) 273 274 if remove_uuid: 275 remove_uuid = remove_uuid.rstrip(',') 276 consistency_group_id = utils.find_resource( 277 volume_client.consistencygroups, 278 parsed_args.consistency_group).id 279 volume_client.consistencygroups.update( 280 consistency_group_id, remove_volumes=remove_uuid) 281 282 283class SetConsistencyGroup(command.Command): 284 _description = _("Set consistency group properties") 285 286 def get_parser(self, prog_name): 287 parser = super(SetConsistencyGroup, self).get_parser(prog_name) 288 parser.add_argument( 289 'consistency_group', 290 metavar='<consistency-group>', 291 help=_('Consistency group to modify (name or ID)') 292 ) 293 parser.add_argument( 294 '--name', 295 metavar='<name>', 296 help=_('New consistency group name'), 297 ) 298 parser.add_argument( 299 '--description', 300 metavar='<description>', 301 help=_('New consistency group description'), 302 ) 303 return parser 304 305 def take_action(self, parsed_args): 306 volume_client = self.app.client_manager.volume 307 kwargs = {} 308 if parsed_args.name: 309 kwargs['name'] = parsed_args.name 310 if parsed_args.description: 311 kwargs['description'] = parsed_args.description 312 if kwargs: 313 consistency_group_id = utils.find_resource( 314 volume_client.consistencygroups, 315 parsed_args.consistency_group).id 316 volume_client.consistencygroups.update( 317 consistency_group_id, **kwargs) 318 319 320class ShowConsistencyGroup(command.ShowOne): 321 _description = _("Display consistency group details.") 322 323 def get_parser(self, prog_name): 324 parser = super(ShowConsistencyGroup, self).get_parser(prog_name) 325 parser.add_argument( 326 "consistency_group", 327 metavar="<consistency-group>", 328 help=_("Consistency group to display (name or ID)") 329 ) 330 return parser 331 332 def take_action(self, parsed_args): 333 volume_client = self.app.client_manager.volume 334 consistency_group = utils.find_resource( 335 volume_client.consistencygroups, 336 parsed_args.consistency_group) 337 return zip(*sorted(consistency_group._info.items())) 338