1# All Rights Reserved.
2#
3#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4#    not use this file except in compliance with the License. You may obtain
5#    a copy of the License at
6#
7#         http://www.apache.org/licenses/LICENSE-2.0
8#
9#    Unless required by applicable law or agreed to in writing, software
10#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12#    License for the specific language governing permissions and limitations
13#    under the License.
14
15import sys
16import time
17
18from cinderclient import exceptions
19from cinderclient import utils
20
21_quota_resources = ['volumes', 'snapshots', 'gigabytes',
22                    'backups', 'backup_gigabytes',
23                    'per_volume_gigabytes', 'groups', ]
24_quota_infos = ['Type', 'In_use', 'Reserved', 'Limit', 'Allocated']
25
26
27def print_volume_image(image_resp_tuple):
28    # image_resp_tuple = tuple (response, body)
29    image = image_resp_tuple[1]
30    vt = image['os-volume_upload_image'].get('volume_type')
31    if vt is not None:
32        image['os-volume_upload_image']['volume_type'] = vt.get('name')
33    utils.print_dict(image['os-volume_upload_image'])
34
35
36def poll_for_status(poll_fn, obj_id, action, final_ok_states,
37                    poll_period=5, show_progress=True):
38    """Blocks while an action occurs. Periodically shows progress."""
39    def print_progress(progress):
40        if show_progress:
41            msg = ('\rInstance %(action)s... %(progress)s%% complete'
42                   % dict(action=action, progress=progress))
43        else:
44            msg = '\rInstance %(action)s...' % dict(action=action)
45
46        sys.stdout.write(msg)
47        sys.stdout.flush()
48
49    print()
50    while True:
51        obj = poll_fn(obj_id)
52        status = obj.status.lower()
53        progress = getattr(obj, 'progress', None) or 0
54        if status in final_ok_states:
55            print_progress(100)
56            print("\nFinished")
57            break
58        elif status == "error":
59            print("\nError %(action)s instance" % {'action': action})
60            break
61        else:
62            print_progress(progress)
63            time.sleep(poll_period)
64
65
66def find_volume_snapshot(cs, snapshot):
67    """Gets a volume snapshot by name or ID."""
68    return utils.find_resource(cs.volume_snapshots, snapshot)
69
70
71def find_vtype(cs, vtype):
72    """Gets a volume type by name or ID."""
73    return utils.find_resource(cs.volume_types, vtype)
74
75
76def find_gtype(cs, gtype):
77    """Gets a group type by name or ID."""
78    return utils.find_resource(cs.group_types, gtype)
79
80
81def find_backup(cs, backup):
82    """Gets a backup by name or ID."""
83    return utils.find_resource(cs.backups, backup)
84
85
86def find_consistencygroup(cs, consistencygroup):
87    """Gets a consistency group by name or ID."""
88    return utils.find_resource(cs.consistencygroups, consistencygroup)
89
90
91def find_group(cs, group, **kwargs):
92    """Gets a group by name or ID."""
93    kwargs['is_group'] = True
94    return utils.find_resource(cs.groups, group, **kwargs)
95
96
97def find_cgsnapshot(cs, cgsnapshot):
98    """Gets a cgsnapshot by name or ID."""
99    return utils.find_resource(cs.cgsnapshots, cgsnapshot)
100
101
102def find_group_snapshot(cs, group_snapshot):
103    """Gets a group_snapshot by name or ID."""
104    return utils.find_resource(cs.group_snapshots, group_snapshot)
105
106
107def find_transfer(cs, transfer):
108    """Gets a transfer by name or ID."""
109    return utils.find_resource(cs.transfers, transfer)
110
111
112def find_qos_specs(cs, qos_specs):
113    """Gets a qos specs by ID."""
114    return utils.find_resource(cs.qos_specs, qos_specs)
115
116
117def find_message(cs, message):
118    """Gets a message by ID."""
119    return utils.find_resource(cs.messages, message)
120
121
122def print_volume_snapshot(snapshot):
123    utils.print_dict(snapshot._info)
124
125
126def translate_keys(collection, convert):
127    for item in collection:
128        keys = item.__dict__
129        for from_key, to_key in convert:
130            if from_key in keys and to_key not in keys:
131                setattr(item, to_key, item._info[from_key])
132
133
134def translate_volume_keys(collection):
135    convert = [('volumeType', 'volume_type'),
136               ('os-vol-tenant-attr:tenant_id', 'tenant_id')]
137    translate_keys(collection, convert)
138
139
140def translate_volume_snapshot_keys(collection):
141    convert = [('volumeId', 'volume_id')]
142    translate_keys(collection, convert)
143
144
145def translate_availability_zone_keys(collection):
146    convert = [('zoneName', 'name'), ('zoneState', 'status')]
147    translate_keys(collection, convert)
148
149
150def extract_filters(args):
151    filters = {}
152    for f in args:
153        if '=' in f:
154            (key, value) = f.split('=', 1)
155            if value.startswith('{') and value.endswith('}'):
156                value = _build_internal_dict(value[1:-1])
157            filters[key] = value
158        else:
159            print("WARNING: Ignoring the filter %s while showing result." % f)
160
161    return filters
162
163
164def _build_internal_dict(content):
165    result = {}
166    for pair in content.split(','):
167        k, v = pair.split(':', 1)
168        result.update({k.strip(): v.strip()})
169    return result
170
171
172def extract_metadata(args, type='user_metadata'):
173    metadata = {}
174    if type == 'image_metadata':
175        args_metadata = args.image_metadata
176    else:
177        args_metadata = args.metadata
178    for metadatum in args_metadata:
179        # unset doesn't require a val, so we have the if/else
180        if '=' in metadatum:
181            (key, value) = metadatum.split('=', 1)
182        else:
183            key = metadatum
184            value = None
185
186        metadata[key] = value
187    return metadata
188
189
190def print_volume_type_list(vtypes):
191    utils.print_list(vtypes, ['ID', 'Name', 'Description', 'Is_Public'])
192
193
194def print_group_type_list(gtypes):
195    utils.print_list(gtypes, ['ID', 'Name', 'Description'])
196
197
198def print_resource_filter_list(filters):
199    formatter = {'Filters': lambda resource: ', '.join(resource.filters)}
200    utils.print_list(filters, ['Resource', 'Filters'], formatters=formatter)
201
202
203def quota_show(quotas):
204    quotas_info_dict = utils.unicode_key_value_to_string(quotas._info)
205    quota_dict = {}
206    for resource in quotas_info_dict.keys():
207        good_name = False
208        for name in _quota_resources:
209            if resource.startswith(name):
210                good_name = True
211        if not good_name:
212            continue
213        quota_dict[resource] = getattr(quotas, resource, None)
214    utils.print_dict(quota_dict)
215
216
217def quota_usage_show(quotas):
218    quota_list = []
219    quotas_info_dict = utils.unicode_key_value_to_string(quotas._info)
220    for resource in quotas_info_dict.keys():
221        good_name = False
222        for name in _quota_resources:
223            if resource.startswith(name):
224                good_name = True
225        if not good_name:
226            continue
227        quota_info = getattr(quotas, resource, None)
228        quota_info['Type'] = resource
229        quota_info = dict((k.capitalize(), v) for k, v in quota_info.items())
230        quota_list.append(quota_info)
231    utils.print_list(quota_list, _quota_infos)
232
233
234def quota_update(manager, identifier, args):
235    updates = {}
236    for resource in _quota_resources:
237        val = getattr(args, resource, None)
238        if val is not None:
239            if args.volume_type:
240                resource = resource + '_%s' % args.volume_type
241            updates[resource] = val
242
243    if updates:
244        skip_validation = getattr(args, 'skip_validation', True)
245        if not skip_validation:
246            updates['skip_validation'] = skip_validation
247        quota_show(manager.update(identifier, **updates))
248    else:
249        msg = 'Must supply at least one quota field to update.'
250        raise exceptions.ClientException(code=1, message=msg)
251
252
253def find_volume_type(cs, vtype):
254    """Gets a volume type by name or ID."""
255    return utils.find_resource(cs.volume_types, vtype)
256
257
258def find_group_type(cs, gtype):
259    """Gets a group type by name or ID."""
260    return utils.find_resource(cs.group_types, gtype)
261
262
263def print_volume_encryption_type_list(encryption_types):
264    """
265    Lists volume encryption types.
266
267    :param encryption_types: a list of :class: VolumeEncryptionType instances
268    """
269    utils.print_list(encryption_types, ['Volume Type ID', 'Provider',
270                                        'Cipher', 'Key Size',
271                                        'Control Location'])
272
273
274def print_qos_specs(qos_specs):
275    # formatters defines field to be converted from unicode to string
276    utils.print_dict(qos_specs._info, formatters=['specs'])
277
278
279def print_qos_specs_list(q_specs):
280    utils.print_list(q_specs, ['ID', 'Name', 'Consumer', 'specs'])
281
282
283def print_qos_specs_and_associations_list(q_specs):
284    utils.print_list(q_specs, ['ID', 'Name', 'Consumer', 'specs'])
285
286
287def print_associations_list(associations):
288    utils.print_list(associations, ['Association_Type', 'Name', 'ID'])
289
290
291def _poll_for_status(poll_fn, obj_id, info, action, final_ok_states,
292                     timeout_period, global_request_id=None, messages=None,
293                     poll_period=2, status_field="status"):
294    """Block while an action is being performed."""
295    time_elapsed = 0
296    while True:
297        time.sleep(poll_period)
298        time_elapsed += poll_period
299        obj = poll_fn(obj_id)
300        status = getattr(obj, status_field)
301        info[status_field] = status
302        if status:
303            status = status.lower()
304
305        if status in final_ok_states:
306            break
307        elif status == "error":
308            utils.print_dict(info)
309            if global_request_id:
310                search_opts = {
311                    'request_id': global_request_id
312                    }
313                message_list = messages.list(search_opts=search_opts)
314                try:
315                    fault_msg = message_list[0].user_message
316                except IndexError:
317                    fault_msg = "Unknown error. Operation failed."
318                raise exceptions.ResourceInErrorState(obj, fault_msg)
319        elif time_elapsed == timeout_period:
320            utils.print_dict(info)
321            raise exceptions.TimeoutException(obj, action)
322