Coverage for manila/db/sqlalchemy/api.py: 87%
3720 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:39 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:39 +0000
1# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
2# Copyright 2010 United States Government as represented by the
3# Administrator of the National Aeronautics and Space Administration.
4# Copyright (c) 2014 Mirantis, Inc.
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
19"""Implementation of SQLAlchemy backend."""
21import copy
22import datetime
23from functools import wraps
24import ipaddress
25import sys
26import warnings
28# NOTE(uglide): Required to override default oslo_db Query class
29import manila.db.sqlalchemy.query # noqa
31from oslo_config import cfg
32from oslo_db import api as oslo_db_api
33from oslo_db import exception as db_exc
34from oslo_db import exception as db_exception
35from oslo_db import options as db_options
36from oslo_db.sqlalchemy import enginefacade
37from oslo_db.sqlalchemy import utils as db_utils
38from oslo_log import log
39from oslo_utils import excutils
40from oslo_utils import importutils
41from oslo_utils import strutils
42from oslo_utils import timeutils
43from oslo_utils import uuidutils
44import sqlalchemy as sa
45from sqlalchemy import and_
46from sqlalchemy import MetaData
47from sqlalchemy import or_
48from sqlalchemy import orm
49from sqlalchemy.sql.expression import false
50from sqlalchemy.sql.expression import literal
51from sqlalchemy.sql.expression import true
52from sqlalchemy.sql import func
54from manila.common import constants
55from manila.db.sqlalchemy import models
56from manila.db.sqlalchemy import utils
57from manila import exception
58from manila.i18n import _
59from manila import quota
61osprofiler_sqlalchemy = importutils.try_import('osprofiler.sqlalchemy')
63CONF = cfg.CONF
64CONF.import_group("profiler", "manila.service")
66LOG = log.getLogger(__name__)
67QUOTAS = quota.QUOTAS
69_DEFAULT_QUOTA_NAME = 'default'
70PER_PROJECT_QUOTAS = []
72_DEFAULT_SQL_CONNECTION = 'sqlite://'
73db_options.set_defaults(cfg.CONF,
74 connection=_DEFAULT_SQL_CONNECTION)
77context_manager = enginefacade.transaction_context()
79context_manager.configure()
81if ( 81 ↛ 86line 81 didn't jump to line 86 because the condition on line 81 was never true
82 osprofiler_sqlalchemy and
83 CONF.profiler.enabled and
84 CONF.profiler.trace_sqlalchemy
85):
86 context_manager.append_on_engine_create(
87 lambda engine: osprofiler_sqlalchemy.add_tracing(sa, engine, "db"))
90def get_engine():
91 return context_manager.writer.get_engine()
94def get_backend():
95 """The backend is this module itself."""
97 return sys.modules[__name__]
100def is_admin_context(context):
101 """Indicates if the request context is an administrator."""
102 if not context: 102 ↛ 103line 102 didn't jump to line 103 because the condition on line 102 was never true
103 warnings.warn(_('Use of empty request context is deprecated'),
104 DeprecationWarning)
105 raise Exception('die')
106 return context.is_admin
109def is_user_context(context):
110 """Indicates if the request context is a normal user."""
111 if not context: 111 ↛ 112line 111 didn't jump to line 112 because the condition on line 111 was never true
112 return False
113 if context.is_admin:
114 return False
115 if not context.user_id or not context.project_id:
116 return False
117 return True
120def authorize_project_context(context, project_id):
121 """Ensures a request has permission to access the given project."""
122 if is_user_context(context):
123 if not context.project_id: 123 ↛ 124line 123 didn't jump to line 124 because the condition on line 123 was never true
124 raise exception.NotAuthorized()
125 elif context.project_id != project_id: 125 ↛ 126line 125 didn't jump to line 126 because the condition on line 125 was never true
126 raise exception.NotAuthorized()
129def authorize_user_context(context, user_id):
130 """Ensures a request has permission to access the given user."""
131 if is_user_context(context):
132 if not context.user_id:
133 raise exception.NotAuthorized()
134 elif context.user_id != user_id:
135 raise exception.NotAuthorized()
138def authorize_quota_class_context(context, class_name):
139 """Ensures a request has permission to access the given quota class."""
140 if is_user_context(context): 140 ↛ 141line 140 didn't jump to line 141 because the condition on line 140 was never true
141 if not context.quota_class:
142 raise exception.NotAuthorized()
143 elif context.quota_class != class_name:
144 raise exception.NotAuthorized()
147def require_admin_context(f):
148 """Decorator to require admin request context.
150 The first argument to the wrapped function must be the context.
152 """
153 @wraps(f)
154 def wrapper(*args, **kwargs):
155 if not is_admin_context(args[0]):
156 raise exception.AdminRequired()
157 return f(*args, **kwargs)
158 return wrapper
161def require_context(f):
162 """Decorator to require *any* user or admin context.
164 This does no authorization for user or project access matching, see
165 :py:func:`authorize_project_context` and
166 :py:func:`authorize_user_context`.
168 The first argument to the wrapped function must be the context.
170 """
171 @wraps(f)
172 def wrapper(*args, **kwargs):
173 if not is_admin_context(args[0]) and not is_user_context(args[0]):
174 raise exception.NotAuthorized()
175 return f(*args, **kwargs)
176 return wrapper
179def require_share_exists(f):
180 """Decorator to require the specified share to exist.
182 Requires the wrapped function to use context and share_id as
183 their first two arguments.
184 """
185 @wraps(f)
186 def wrapper(context, share_id, *args, **kwargs):
187 share_get(context, share_id)
188 return f(context, share_id, *args, **kwargs)
189 wrapper.__name__ = f.__name__
190 return wrapper
193def require_share_snapshot_exists(f):
194 """Decorator to require the specified share snapshot to exist.
196 Requires the wrapped function to use context and share_snapshot_id as
197 their first two arguments.
198 """
199 @wraps(f)
200 def wrapper(context, share_snapshot_id, *args, **kwargs):
201 share_snapshot_get(context, share_snapshot_id)
202 return f(context, share_snapshot_id, *args, **kwargs)
203 wrapper.__name__ = f.__name__
204 return wrapper
207def require_share_network_subnet_exists(f):
208 """Decorator to require the specified share network subnet to exist.
210 Requires the wrapped function to use context and share_network_subnet_id
211 as their first two arguments.
212 """
213 @wraps(f)
214 def wrapper(context, share_network_subnet_id, *args, **kwargs):
215 share_network_subnet_get(context, share_network_subnet_id)
216 return f(context, share_network_subnet_id, *args, **kwargs)
217 wrapper.__name__ = f.__name__
218 return wrapper
221def require_share_instance_exists(f):
222 """Decorator to require the specified share instance to exist.
224 Requires the wrapped function to use context and share_instance_id as
225 their first two arguments.
226 """
227 @wraps(f)
228 def wrapper(context, share_instance_id, *args, **kwargs):
229 share_instance_get(context, share_instance_id)
230 return f(context, share_instance_id, *args, **kwargs)
231 wrapper.__name__ = f.__name__
232 return wrapper
235def require_availability_zone_exists(*, strict: bool):
236 """Decorator to require the specified availability zone to exist.
238 Requires the wrapped function to use context as their first argument and
239 values as either their second (for create) or third (for update) argument.
241 .. note::
243 This has a side-effect of updating the provided values dict, replacing
244 ``availability_zone`` with ``availability_zone_id``
246 :param strict: If true, ``values`` must contain an ``availability_zone``
247 key
248 """
249 def inner(f):
250 @wraps(f)
251 def wrapper(context, *args, **kwargs):
252 values = args[0]
253 if not isinstance(args[0], dict):
254 values = args[1]
255 ensure_availability_zone_exists(context, values, strict=strict)
256 return f(context, *args, **kwargs)
257 wrapper.__name__ = f.__name__
258 return wrapper
259 return inner
262def apply_sorting(model, query, sort_key, sort_dir):
263 if sort_dir.lower() not in ('desc', 'asc'): 263 ↛ 264line 263 didn't jump to line 264 because the condition on line 263 was never true
264 msg = _("Wrong sorting data provided: sort key is '%(sort_key)s' "
265 "and sort direction is '%(sort_dir)s'.") % {
266 "sort_key": sort_key, "sort_dir": sort_dir}
267 raise exception.InvalidInput(reason=msg)
269 # NOTE(maaoyu): We add the additional sort by ID in this case to
270 # get deterministic results. Without the ordering by ID this could
271 # lead to flapping return lists.
272 sort_keys = [sort_key]
273 if sort_key != 'id':
274 sort_keys.append('id')
276 for sort_key in sort_keys:
277 sort_attr = getattr(model, sort_key)
278 sort_method = getattr(sort_attr, sort_dir.lower())
279 query = query.order_by(sort_method())
281 return query
284def handle_db_data_error(f):
285 def wrapper(*args, **kwargs):
286 try:
287 return f(*args, **kwargs)
288 except db_exc.DBDataError:
289 msg = _('Error writing field to database.')
290 LOG.exception(msg)
291 raise exception.Invalid(msg)
293 return wrapper
296def model_query(context, model, *args, **kwargs):
297 """Query helper that accounts for context's `read_deleted` field.
299 :param context: context to query under
300 :param model: model to query. Must be a subclass of ModelBase.
301 :param read_deleted: if present, overrides context's read_deleted field.
302 :param project_only: if present and context is user-type, then restrict
303 query to match the context's project_id.
304 """
305 assert 'session' not in kwargs
306 assert hasattr(context, 'session') and context.session
308 read_deleted = kwargs.get('read_deleted') or context.read_deleted
309 project_only = kwargs.get('project_only')
310 kwargs = dict()
312 if project_only and not context.is_admin:
313 kwargs['project_id'] = context.project_id
314 if read_deleted in ('no', 'n', False):
315 kwargs['deleted'] = False
316 elif read_deleted == 'only':
317 kwargs['deleted'] = True
318 elif read_deleted in ('yes', 'y', True): 318 ↛ 321line 318 didn't jump to line 321 because the condition on line 318 was always true
319 pass
321 return db_utils.model_query(
322 model=model,
323 session=context.session,
324 args=args,
325 **kwargs,
326 )
329def _process_model_like_filter(model, query, filters):
330 """Applies regex expression filtering to a query.
332 :param model: model to apply filters to
333 :param query: query to apply filters to
334 :param filters: dictionary of filters with regex values
335 :returns: the updated query.
336 """
337 if query is None: 337 ↛ 338line 337 didn't jump to line 338 because the condition on line 337 was never true
338 return query
340 if filters:
341 for key in sorted(filters):
342 column_attr = getattr(model, key)
343 if 'property' == type(column_attr).__name__: 343 ↛ 344line 343 didn't jump to line 344 because the condition on line 343 was never true
344 continue
345 value = filters[key]
346 if not (isinstance(value, (str, int))): 346 ↛ 347line 346 didn't jump to line 347 because the condition on line 346 was never true
347 continue
348 query = query.filter(
349 column_attr.op('LIKE')(u'%%%s%%' % value))
350 return query
353def apply_like_filters(process_exact_filters):
354 def _decorator(query, model, filters, legal_keys):
355 exact_filters = filters.copy()
356 regex_filters = {}
357 for key, value in filters.items():
358 if key not in legal_keys:
359 # Skip ones we're not filtering on
360 continue
361 # NOTE(haixin): For inexact match, the filter keys
362 # are in the format of 'key~=value'
363 if key.endswith('~'):
364 exact_filters.pop(key)
365 regex_filters[key.rstrip('~')] = value
366 query = process_exact_filters(query, model, exact_filters,
367 legal_keys)
368 return _process_model_like_filter(model, query, regex_filters)
369 return _decorator
372@apply_like_filters
373def exact_filter(query, model, filters, legal_keys,
374 created_at_key='created_at'):
375 """Applies exact match filtering to a query.
377 Returns the updated query. Modifies filters argument to remove
378 filters consumed.
380 :param query: query to apply filters to
381 :param model: model object the query applies to, for IN-style
382 filtering
383 :param filters: dictionary of filters; values that are lists,
384 tuples, sets, or frozensets cause an 'IN' test to
385 be performed, while exact matching ('==' operator)
386 is used for other values
387 :param legal_keys: list of keys to apply exact filtering to
388 """
390 filter_dict = {}
391 created_at_attr = getattr(model, created_at_key, None)
392 # Walk through all the keys
393 for key in legal_keys:
394 # Skip ones we're not filtering on
395 if key not in filters:
396 continue
398 # OK, filtering on this key; what value do we search for?
399 value = filters.pop(key)
401 if key == 'created_since' and created_at_attr:
402 # This is a reserved query parameter to indicate resources created
403 # after a particular datetime
404 value = timeutils.normalize_time(value)
405 query = query.filter(created_at_attr.op('>=')(value))
406 elif key == 'created_before' and created_at_attr:
407 # This is a reserved query parameter to indicate resources created
408 # before a particular datetime
409 value = timeutils.normalize_time(value)
410 query = query.filter(created_at_attr.op('<=')(value))
411 elif isinstance(value, (list, tuple, set, frozenset)):
412 # Looking for values in a list; apply to query directly
413 column_attr = getattr(model, key)
414 query = query.filter(column_attr.in_(value))
415 else:
416 # OK, simple exact match; save for later
417 filter_dict[key] = value
419 # Apply simple exact matches
420 if filter_dict:
421 query = query.filter_by(**filter_dict)
423 return query
426def ensure_model_dict_has_id(model_dict):
427 if not model_dict.get('id'):
428 model_dict['id'] = uuidutils.generate_uuid()
429 return model_dict
432def _sync_shares(context, project_id, user_id, share_type_id=None):
433 shares, _ = _share_data_get_for_project(
434 context, project_id, user_id, share_type_id=share_type_id,
435 )
436 return {'shares': shares}
439def _sync_snapshots(context, project_id, user_id, share_type_id=None):
440 snapshots, _ = _snapshot_data_get_for_project(
441 context, project_id, user_id, share_type_id=share_type_id,
442 )
443 return {'snapshots': snapshots}
446def _sync_gigabytes(context, project_id, user_id, share_type_id=None):
447 _, share_gigs = _share_data_get_for_project(
448 context, project_id, user_id, share_type_id=share_type_id,
449 )
450 return {'gigabytes': share_gigs}
453def _sync_snapshot_gigabytes(context, project_id, user_id, share_type_id=None):
454 _, snapshot_gigs = _snapshot_data_get_for_project(
455 context, project_id, user_id, share_type_id=share_type_id,
456 )
457 return {'snapshot_gigabytes': snapshot_gigs}
460def _sync_share_networks(context, project_id, user_id, share_type_id=None):
461 share_networks_count = _count_share_networks(
462 context, project_id, user_id, share_type_id=share_type_id,
463 )
464 return {'share_networks': share_networks_count}
467def _sync_share_groups(context, project_id, user_id, share_type_id=None):
468 share_groups_count = _count_share_groups(
469 context, project_id, user_id, share_type_id=share_type_id,
470 )
471 return {'share_groups': share_groups_count}
474def _sync_backups(context, project_id, user_id, share_type_id=None):
475 backups, _ = _backup_data_get_for_project(context, project_id, user_id)
476 return {'backups': backups}
479def _sync_backup_gigabytes(context, project_id, user_id, share_type_id=None):
480 _, backup_gigs = _backup_data_get_for_project(context, project_id, user_id)
481 return {'backup_gigabytes': backup_gigs}
484def _sync_share_group_snapshots(
485 context, project_id, user_id, share_type_id=None,
486):
487 share_group_snapshots_count = _count_share_group_snapshots(
488 context, project_id, user_id, share_type_id=share_type_id,
489 )
490 return {'share_group_snapshots': share_group_snapshots_count}
493def _sync_share_replicas(context, project_id, user_id, share_type_id=None):
494 share_replicas_count, _ = _share_replica_data_get_for_project(
495 context, project_id, user_id, share_type_id=share_type_id,
496 )
497 return {'share_replicas': share_replicas_count}
500def _sync_replica_gigabytes(context, project_id, user_id, share_type_id=None):
501 _, replica_gigs = _share_replica_data_get_for_project(
502 context, project_id, user_id, share_type_id=share_type_id,
503 )
504 return {'replica_gigabytes': replica_gigs}
507def _sync_encryption_keys(context, project_id, user_id, share_type_id=None):
508 encryption_keys = _count_encryption_keys_for_project(
509 context, project_id, user_id
510 )
511 return {'encryption_keys': encryption_keys}
514QUOTA_SYNC_FUNCTIONS = {
515 '_sync_shares': _sync_shares,
516 '_sync_snapshots': _sync_snapshots,
517 '_sync_gigabytes': _sync_gigabytes,
518 '_sync_snapshot_gigabytes': _sync_snapshot_gigabytes,
519 '_sync_share_networks': _sync_share_networks,
520 '_sync_share_groups': _sync_share_groups,
521 '_sync_share_group_snapshots': _sync_share_group_snapshots,
522 '_sync_share_replicas': _sync_share_replicas,
523 '_sync_replica_gigabytes': _sync_replica_gigabytes,
524 '_sync_backups': _sync_backups,
525 '_sync_backup_gigabytes': _sync_backup_gigabytes,
526 '_sync_encryption_keys': _sync_encryption_keys,
527}
530###################
532@require_admin_context
533@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
534@context_manager.writer
535def share_resources_host_update(context, current_host, new_host):
536 """Updates the 'host' attribute of resources"""
538 resources = {
539 'instances': models.ShareInstance,
540 'servers': models.ShareServer,
541 'groups': models.ShareGroup,
542 }
543 result = {}
545 for res_name, res_model in resources.items():
546 host_field = res_model.host
547 query = model_query(
548 context, res_model, read_deleted="no",
549 ).filter(host_field.like('{}%'.format(current_host)))
550 count = query.update(
551 {host_field: func.replace(host_field, current_host, new_host)},
552 synchronize_session=False,
553 )
554 result.update({res_name: count})
555 return result
558###################
561@require_admin_context
562@context_manager.writer
563def service_destroy(context, service_id):
564 service_ref = _service_get(context, service_id)
565 service_ref.soft_delete(context.session)
568@require_admin_context
569def _service_get(context, service_id):
570 result = (
571 model_query(
572 context,
573 models.Service,
574 ).filter_by(
575 id=service_id,
576 ).first()
577 )
578 if not result: 578 ↛ 579line 578 didn't jump to line 579 because the condition on line 578 was never true
579 raise exception.ServiceNotFound(service_id=service_id)
581 return result
584@require_admin_context
585@context_manager.reader
586def service_get(context, service_id):
587 return _service_get(context, service_id)
590@require_admin_context
591@context_manager.reader
592def service_get_all(context, disabled=None):
593 query = model_query(context, models.Service)
595 if disabled is not None:
596 query = query.filter_by(disabled=disabled)
598 return query.all()
601@require_admin_context
602@context_manager.reader
603def service_get_all_by_topic(context, topic, consider_disabled=False):
604 query = model_query(
605 context, models.Service, read_deleted="no")
607 if not consider_disabled: 607 ↛ 610line 607 didn't jump to line 610 because the condition on line 607 was always true
608 query = query.filter_by(disabled=False)
610 return query.filter_by(topic=topic).all()
613@require_admin_context
614@context_manager.reader
615def service_get_by_host_and_topic(context, host, topic):
616 result = (model_query(
617 context, models.Service, read_deleted="no").
618 filter_by(disabled=False).
619 filter_by(host=host).
620 filter_by(topic=topic).
621 first())
622 if not result: 622 ↛ 624line 622 didn't jump to line 624 because the condition on line 622 was always true
623 raise exception.ServiceNotFound(service_id=host)
624 return result
627@require_admin_context
628def _service_get_all_topic_subquery(context, topic, subq, label):
629 sort_value = getattr(subq.c, label)
630 return (
631 model_query(
632 context, models.Service,
633 func.coalesce(sort_value, 0),
634 read_deleted="no",
635 ).filter_by(
636 topic=topic,
637 ).filter_by(
638 disabled=False,
639 ).outerjoin(
640 (subq, models.Service.host == subq.c.host)
641 ).order_by(
642 sort_value
643 ).all()
644 )
647@require_admin_context
648@context_manager.reader
649def service_get_all_share_sorted(context):
650 topic = CONF.share_topic
651 label = 'share_gigabytes'
652 subq = (
653 model_query(
654 context,
655 models.Share,
656 func.sum(models.Share.size).label(label),
657 read_deleted="no",
658 ).join(
659 models.ShareInstance,
660 models.ShareInstance.share_id == models.Share.id,
661 ).group_by(
662 models.ShareInstance.host
663 ).subquery()
664 )
665 return _service_get_all_topic_subquery(
666 context,
667 topic,
668 subq,
669 label,
670 )
673@require_admin_context
674@context_manager.reader
675def service_get_by_args(context, host, binary):
676 result = (model_query(context, models.Service).
677 filter_by(host=host).
678 filter_by(binary=binary).
679 first())
681 if not result: 681 ↛ 684line 681 didn't jump to line 684 because the condition on line 681 was always true
682 raise exception.HostBinaryNotFound(host=host, binary=binary)
684 return result
687@require_admin_context
688@require_availability_zone_exists(strict=True)
689@context_manager.writer
690def service_create(context, values):
691 service_ref = models.Service()
692 service_ref.update(values)
693 if not CONF.enable_new_services:
694 service_ref.disabled = True
696 service_ref.save(context.session)
697 return service_ref
700@require_admin_context
701@require_availability_zone_exists(strict=False)
702@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
703@context_manager.writer
704def service_update(context, service_id, values):
705 service_ref = _service_get(context, service_id)
706 service_ref.update(values)
707 service_ref.save(context.session)
710###################
713@require_context
714@context_manager.reader.independent
715def quota_get_all_by_project_and_user(context, project_id, user_id):
716 authorize_project_context(context, project_id)
717 user_quotas = model_query(
718 context, models.ProjectUserQuota,
719 models.ProjectUserQuota.resource,
720 models.ProjectUserQuota.hard_limit,
721 ).filter_by(
722 project_id=project_id,
723 ).filter_by(
724 user_id=user_id,
725 ).all()
727 result = {'project_id': project_id, 'user_id': user_id}
728 for u_quota in user_quotas:
729 result[u_quota.resource] = u_quota.hard_limit
730 return result
733@require_context
734@context_manager.reader.independent
735def quota_get_all_by_project_and_share_type(
736 context, project_id, share_type_id,
737):
738 authorize_project_context(context, project_id)
739 share_type_quotas = model_query(
740 context, models.ProjectShareTypeQuota,
741 models.ProjectShareTypeQuota.resource,
742 models.ProjectShareTypeQuota.hard_limit,
743 ).filter_by(
744 project_id=project_id,
745 ).filter_by(
746 share_type_id=share_type_id,
747 ).all()
749 result = {
750 'project_id': project_id,
751 'share_type_id': share_type_id,
752 }
753 for st_quota in share_type_quotas:
754 result[st_quota.resource] = st_quota.hard_limit
755 return result
758@require_context
759@context_manager.reader.independent
760def quota_get_all_by_project(context, project_id):
761 authorize_project_context(context, project_id)
762 project_quotas = model_query(
763 context, models.Quota, read_deleted="no",
764 ).filter_by(
765 project_id=project_id,
766 ).all()
768 result = {'project_id': project_id}
769 for p_quota in project_quotas:
770 result[p_quota.resource] = p_quota.hard_limit
771 return result
774@require_context
775@context_manager.reader.independent
776def quota_get_all(context, project_id):
777 authorize_project_context(context, project_id)
779 result = (model_query(context, models.ProjectUserQuota).
780 filter_by(project_id=project_id).
781 all())
783 return result
786@require_admin_context
787@context_manager.writer.independent
788def quota_create(
789 context,
790 project_id,
791 resource,
792 limit,
793 user_id=None,
794 share_type_id=None,
795):
796 per_user = user_id and resource not in PER_PROJECT_QUOTAS
798 if per_user:
799 check = model_query(context, models.ProjectUserQuota).filter(
800 models.ProjectUserQuota.project_id == project_id,
801 models.ProjectUserQuota.user_id == user_id,
802 models.ProjectUserQuota.resource == resource,
803 ).all()
804 quota_ref = models.ProjectUserQuota()
805 quota_ref.user_id = user_id
806 elif share_type_id:
807 check = model_query(context, models.ProjectShareTypeQuota).filter(
808 models.ProjectShareTypeQuota.project_id == project_id,
809 models.ProjectShareTypeQuota.share_type_id == share_type_id,
810 models.ProjectShareTypeQuota.resource == resource,
811 ).all()
812 quota_ref = models.ProjectShareTypeQuota()
813 quota_ref.share_type_id = share_type_id
814 else:
815 check = model_query(context, models.Quota).filter(
816 models.Quota.project_id == project_id,
817 models.Quota.resource == resource,
818 ).all()
819 quota_ref = models.Quota()
820 if check: 820 ↛ 821line 820 didn't jump to line 821 because the condition on line 820 was never true
821 raise exception.QuotaExists(project_id=project_id, resource=resource)
823 quota_ref.project_id = project_id
824 quota_ref.resource = resource
825 quota_ref.hard_limit = limit
826 try:
827 quota_ref.save(context.session)
828 except Exception as e:
829 if "out of range" in str(e).lower():
830 msg = _("Quota limit should not exceed 2147483647")
831 raise exception.InvalidInput(reason=msg)
832 raise
833 return quota_ref
836@require_admin_context
837@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
838@context_manager.writer.independent
839def quota_update(
840 context,
841 project_id,
842 resource,
843 limit,
844 user_id=None,
845 share_type_id=None,
846):
847 per_user = user_id and resource not in PER_PROJECT_QUOTAS
848 if per_user:
849 query = model_query(context, models.ProjectUserQuota).filter(
850 models.ProjectUserQuota.project_id == project_id,
851 models.ProjectUserQuota.user_id == user_id,
852 models.ProjectUserQuota.resource == resource,
853 )
854 elif share_type_id:
855 query = model_query(context, models.ProjectShareTypeQuota).filter(
856 models.ProjectShareTypeQuota.project_id == project_id,
857 models.ProjectShareTypeQuota.share_type_id == share_type_id,
858 models.ProjectShareTypeQuota.resource == resource,
859 )
860 else:
861 query = model_query(context, models.Quota).filter(
862 models.Quota.project_id == project_id,
863 models.Quota.resource == resource,
864 )
866 result = query.update({'hard_limit': limit})
867 if not result:
868 if per_user:
869 raise exception.ProjectUserQuotaNotFound(
870 project_id=project_id, user_id=user_id)
871 elif share_type_id:
872 raise exception.ProjectShareTypeQuotaNotFound(
873 project_id=project_id, share_type=share_type_id)
874 raise exception.ProjectQuotaNotFound(project_id=project_id)
877###################
880@require_context
881@context_manager.reader
882def quota_class_get(context, class_name, resource):
883 result = (
884 model_query(
885 context,
886 models.QuotaClass,
887 read_deleted="no",
888 ).filter_by(
889 class_name=class_name
890 ).filter_by(
891 resource=resource
892 ).first()
893 )
895 if not result:
896 raise exception.QuotaClassNotFound(class_name=class_name)
898 return result
901@require_context
902@context_manager.reader
903def quota_class_get_default(context):
904 rows = (model_query(context, models.QuotaClass, read_deleted="no").
905 filter_by(class_name=_DEFAULT_QUOTA_NAME).
906 all())
908 result = {'class_name': _DEFAULT_QUOTA_NAME}
909 for row in rows: 909 ↛ 910line 909 didn't jump to line 910 because the loop on line 909 never started
910 result[row.resource] = row.hard_limit
912 return result
915@require_context
916@context_manager.reader
917def quota_class_get_all_by_name(context, class_name):
918 authorize_quota_class_context(context, class_name)
920 rows = (model_query(context, models.QuotaClass, read_deleted="no").
921 filter_by(class_name=class_name).
922 all())
924 result = {'class_name': class_name}
925 for row in rows:
926 result[row.resource] = row.hard_limit
928 return result
931@require_admin_context
932@context_manager.writer
933def quota_class_create(context, class_name, resource, limit):
934 quota_class_ref = models.QuotaClass()
935 quota_class_ref.class_name = class_name
936 quota_class_ref.resource = resource
937 quota_class_ref.hard_limit = limit
938 quota_class_ref.save(context.session)
939 return quota_class_ref
942@require_admin_context
943@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
944@context_manager.writer
945def quota_class_update(context, class_name, resource, limit):
946 result = (model_query(context, models.QuotaClass, read_deleted="no").
947 filter_by(class_name=class_name).
948 filter_by(resource=resource).
949 update({'hard_limit': limit}))
951 if not result: 951 ↛ exitline 951 didn't return from function 'quota_class_update' because the condition on line 951 was always true
952 raise exception.QuotaClassNotFound(class_name=class_name)
955###################
958@require_context
959@context_manager.reader
960def quota_usage_get(context, project_id, resource, user_id=None,
961 share_type_id=None):
962 query = (model_query(context, models.QuotaUsage, read_deleted="no").
963 filter_by(project_id=project_id).
964 filter_by(resource=resource))
965 if user_id: 965 ↛ 966line 965 didn't jump to line 966 because the condition on line 965 was never true
966 if resource not in PER_PROJECT_QUOTAS:
967 result = query.filter_by(user_id=user_id).first()
968 else:
969 result = query.filter_by(user_id=None).first()
970 elif share_type_id: 970 ↛ 971line 970 didn't jump to line 971 because the condition on line 970 was never true
971 result = query.filter_by(queryshare_type_id=share_type_id).first()
972 else:
973 result = query.first()
975 if not result:
976 raise exception.QuotaUsageNotFound(project_id=project_id)
978 return result
981def _quota_usage_get_all(context, project_id, user_id=None,
982 share_type_id=None):
983 authorize_project_context(context, project_id)
984 query = (model_query(context, models.QuotaUsage, read_deleted="no").
985 filter_by(project_id=project_id))
986 result = {'project_id': project_id}
987 if user_id:
988 query = query.filter(or_(models.QuotaUsage.user_id == user_id,
989 models.QuotaUsage.user_id is None))
990 result['user_id'] = user_id
991 elif share_type_id:
992 query = query.filter_by(share_type_id=share_type_id)
993 result['share_type_id'] = share_type_id
994 else:
995 query = query.filter_by(share_type_id=None)
997 rows = query.all()
998 for row in rows:
999 if row.resource in result: 999 ↛ 1000line 999 didn't jump to line 1000 because the condition on line 999 was never true
1000 result[row.resource]['in_use'] += row.in_use
1001 result[row.resource]['reserved'] += row.reserved
1002 else:
1003 result[row.resource] = dict(in_use=row.in_use,
1004 reserved=row.reserved)
1006 return result
1009@require_context
1010@context_manager.reader
1011def quota_usage_get_all_by_project(context, project_id):
1012 return _quota_usage_get_all(context, project_id)
1015@require_context
1016@context_manager.reader
1017def quota_usage_get_all_by_project_and_user(context, project_id, user_id):
1018 return _quota_usage_get_all(context, project_id, user_id=user_id)
1021@require_context
1022@context_manager.reader
1023def quota_usage_get_all_by_project_and_share_type(context, project_id,
1024 share_type_id):
1025 return _quota_usage_get_all(
1026 context, project_id, share_type_id=share_type_id)
1029def _quota_usage_create(context, project_id, user_id, resource, in_use,
1030 reserved, until_refresh, share_type_id=None):
1031 quota_usage_ref = models.QuotaUsage()
1032 if share_type_id:
1033 quota_usage_ref.share_type_id = share_type_id
1034 else:
1035 quota_usage_ref.user_id = user_id
1036 quota_usage_ref.project_id = project_id
1037 quota_usage_ref.resource = resource
1038 quota_usage_ref.in_use = in_use
1039 quota_usage_ref.reserved = reserved
1040 quota_usage_ref.until_refresh = until_refresh
1041 # updated_at is needed for judgement of max_age
1042 quota_usage_ref.updated_at = timeutils.utcnow()
1044 quota_usage_ref.save(session=context.session)
1046 return quota_usage_ref
1049@require_admin_context
1050@context_manager.writer
1051def quota_usage_create(context, project_id, user_id, resource, in_use,
1052 reserved, until_refresh, share_type_id=None):
1053 return _quota_usage_create(
1054 context,
1055 project_id,
1056 user_id,
1057 resource,
1058 in_use,
1059 reserved,
1060 until_refresh,
1061 share_type_id=share_type_id,
1062 )
1065@require_admin_context
1066@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
1067@context_manager.writer.independent
1068def quota_usage_update(context, project_id, user_id, resource,
1069 share_type_id=None, **kwargs):
1070 updates = {}
1071 for key in ('in_use', 'reserved', 'until_refresh'):
1072 if key in kwargs:
1073 updates[key] = kwargs[key]
1075 query = model_query(
1076 context, models.QuotaUsage, read_deleted="no",
1077 ).filter_by(project_id=project_id).filter_by(resource=resource)
1078 if share_type_id:
1079 query = query.filter_by(share_type_id=share_type_id)
1080 else:
1081 query = query.filter(or_(models.QuotaUsage.user_id == user_id,
1082 models.QuotaUsage.user_id is None))
1083 result = query.update(updates)
1085 if not result:
1086 raise exception.QuotaUsageNotFound(project_id=project_id)
1089###################
1092def _reservation_create(context, uuid, usage, project_id, user_id, resource,
1093 delta, expire, share_type_id=None):
1094 reservation_ref = models.Reservation()
1095 reservation_ref.uuid = uuid
1096 reservation_ref.usage_id = usage['id']
1097 reservation_ref.project_id = project_id
1098 if share_type_id:
1099 reservation_ref.share_type_id = share_type_id
1100 else:
1101 reservation_ref.user_id = user_id
1102 reservation_ref.resource = resource
1103 reservation_ref.delta = delta
1104 reservation_ref.expire = expire
1105 reservation_ref.save(session=context.session)
1106 return reservation_ref
1109###################
1112# NOTE(johannes): The quota code uses SQL locking to ensure races don't
1113# cause under or over counting of resources. To avoid deadlocks, this
1114# code always acquires the lock on quota_usages before acquiring the lock
1115# on reservations.
1117def _get_share_type_quota_usages(context, project_id, share_type_id):
1118 rows = model_query(
1119 context, models.QuotaUsage, read_deleted="no",
1120 ).filter(
1121 models.QuotaUsage.project_id == project_id,
1122 models.QuotaUsage.share_type_id == share_type_id,
1123 ).with_for_update().all()
1124 return {row.resource: row for row in rows}
1127def _get_user_quota_usages(context, project_id, user_id):
1128 # Broken out for testability
1129 rows = model_query(
1130 context, models.QuotaUsage, read_deleted="no",
1131 ).filter_by(
1132 project_id=project_id,
1133 ).filter(
1134 or_(
1135 models.QuotaUsage.user_id == user_id,
1136 models.QuotaUsage.user_id is None,
1137 )
1138 ).with_for_update().all()
1139 return {row.resource: row for row in rows}
1142def _get_project_quota_usages(context, project_id):
1143 rows = model_query(
1144 context, models.QuotaUsage, read_deleted="no",
1145 ).filter_by(
1146 project_id=project_id,
1147 ).filter(
1148 models.QuotaUsage.share_type_id is None,
1149 ).with_for_update().all()
1150 result = dict()
1151 # Get the total count of in_use,reserved
1152 for row in rows: 1152 ↛ 1153line 1152 didn't jump to line 1153 because the loop on line 1152 never started
1153 if row.resource in result:
1154 result[row.resource]['in_use'] += row.in_use
1155 result[row.resource]['reserved'] += row.reserved
1156 result[row.resource]['total'] += (row.in_use + row.reserved)
1157 else:
1158 result[row.resource] = dict(
1159 in_use=row.in_use,
1160 reserved=row.reserved,
1161 total=row.in_use + row.reserved,
1162 )
1163 return result
1166# NOTE(stephenfin): We intentionally don't wrap the outer function here since
1167# we call the innter function multiple times and want each call to be in a
1168# separate transaction
1169@require_context
1170def quota_reserve(context, resources, project_quotas, user_quotas,
1171 share_type_quotas, deltas, expire, until_refresh,
1172 max_age, project_id=None, user_id=None, share_type_id=None,
1173 overquota_allowed=False):
1174 user_reservations = _quota_reserve(
1175 context, resources, project_quotas, user_quotas,
1176 deltas, expire, until_refresh, max_age, project_id, user_id=user_id,
1177 overquota_allowed=overquota_allowed)
1178 if share_type_id:
1179 try:
1180 st_reservations = _quota_reserve(
1181 context, resources, project_quotas, share_type_quotas,
1182 deltas, expire, until_refresh, max_age, project_id,
1183 share_type_id=share_type_id,
1184 overquota_allowed=overquota_allowed)
1185 except exception.OverQuota:
1186 # rollback previous reservations
1187 with excutils.save_and_reraise_exception():
1188 # We call a public method since we haven't wrapped this, the
1189 # caller, and we want to run in a different transaction
1190 reservation_rollback(
1191 context, user_reservations,
1192 project_id=project_id, user_id=user_id)
1193 return user_reservations + st_reservations
1194 return user_reservations
1197# NOTE(stephenfin): Per above, we wrap the inner method here
1198@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
1199@context_manager.writer.independent
1200def _quota_reserve(context, resources, project_quotas, user_or_st_quotas,
1201 deltas, expire, until_refresh,
1202 max_age, project_id=None, user_id=None, share_type_id=None,
1203 overquota_allowed=False):
1204 elevated = context.elevated()
1206 if project_id is None:
1207 project_id = context.project_id
1208 if share_type_id:
1209 user_or_st_usages = _get_share_type_quota_usages(
1210 context, project_id, share_type_id,
1211 )
1212 else:
1213 user_id = user_id if user_id else context.user_id
1214 user_or_st_usages = _get_user_quota_usages(
1215 context, project_id, user_id,
1216 )
1218 # Get the current usages
1219 project_usages = _get_project_quota_usages(context, project_id)
1221 # Handle usage refresh
1222 work = set(deltas.keys())
1223 while work:
1224 resource = work.pop()
1226 # Do we need to refresh the usage?
1227 refresh = False
1228 if ((resource not in PER_PROJECT_QUOTAS) and 1228 ↛ 1240line 1228 didn't jump to line 1240 because the condition on line 1228 was always true
1229 (resource not in user_or_st_usages)):
1230 user_or_st_usages[resource] = _quota_usage_create(
1231 elevated,
1232 project_id,
1233 user_id,
1234 resource,
1235 0, 0,
1236 until_refresh or None,
1237 share_type_id=share_type_id,
1238 )
1239 refresh = True
1240 elif ((resource in PER_PROJECT_QUOTAS) and
1241 (resource not in user_or_st_usages)):
1242 user_or_st_usages[resource] = _quota_usage_create(
1243 elevated,
1244 project_id,
1245 None,
1246 resource,
1247 0, 0,
1248 until_refresh or None,
1249 share_type_id=share_type_id,
1250 )
1251 refresh = True
1252 elif user_or_st_usages[resource].in_use < 0:
1253 # Negative in_use count indicates a desync, so try to
1254 # heal from that...
1255 refresh = True
1256 elif user_or_st_usages[resource].until_refresh is not None:
1257 user_or_st_usages[resource].until_refresh -= 1
1258 if user_or_st_usages[resource].until_refresh <= 0:
1259 refresh = True
1260 elif max_age and (user_or_st_usages[resource].updated_at -
1261 timeutils.utcnow()).seconds >= max_age:
1262 refresh = True
1264 # OK, refresh the usage
1265 if refresh: 1265 ↛ 1223line 1265 didn't jump to line 1223 because the condition on line 1265 was always true
1266 # Grab the sync routine
1267 sync = QUOTA_SYNC_FUNCTIONS[resources[resource].sync]
1269 updates = sync(
1270 elevated,
1271 project_id,
1272 user_id,
1273 share_type_id=share_type_id,
1274 )
1275 for res, in_use in updates.items():
1276 # Make sure we have a destination for the usage!
1277 if ((res not in PER_PROJECT_QUOTAS) and 1277 ↛ 1279line 1277 didn't jump to line 1279 because the condition on line 1277 was never true
1278 (res not in user_or_st_usages)):
1279 user_or_st_usages[res] = _quota_usage_create(
1280 elevated,
1281 project_id,
1282 user_id,
1283 res,
1284 0, 0,
1285 until_refresh or None,
1286 share_type_id=share_type_id,
1287 )
1288 if ((res in PER_PROJECT_QUOTAS) and 1288 ↛ 1290line 1288 didn't jump to line 1290 because the condition on line 1288 was never true
1289 (res not in user_or_st_usages)):
1290 user_or_st_usages[res] = _quota_usage_create(
1291 elevated,
1292 project_id,
1293 None,
1294 res,
1295 0, 0,
1296 until_refresh or None,
1297 share_type_id=share_type_id,
1298 )
1300 if user_or_st_usages[res].in_use != in_use:
1301 LOG.debug(
1302 'quota_usages out of sync, updating. '
1303 'project_id: %(project_id)s, '
1304 'user_id: %(user_id)s, '
1305 'share_type_id: %(share_type_id)s, '
1306 'resource: %(res)s, '
1307 'tracked usage: %(tracked_use)s, '
1308 'actual usage: %(in_use)s',
1309 {'project_id': project_id,
1310 'user_id': user_id,
1311 'share_type_id': share_type_id,
1312 'res': res,
1313 'tracked_use': user_or_st_usages[res].in_use,
1314 'in_use': in_use})
1316 # Update the usage
1317 user_or_st_usages[res].in_use = in_use
1318 user_or_st_usages[res].until_refresh = (
1319 until_refresh or None)
1321 # Because more than one resource may be refreshed
1322 # by the call to the sync routine, and we don't
1323 # want to double-sync, we make sure all refreshed
1324 # resources are dropped from the work set.
1325 work.discard(res)
1327 # NOTE(Vek): We make the assumption that the sync
1328 # routine actually refreshes the
1329 # resources that it is the sync routine
1330 # for. We don't check, because this is
1331 # a best-effort mechanism.
1333 # Check for deltas that would go negative
1334 unders = [res for res, delta in deltas.items()
1335 if delta < 0 and
1336 delta + user_or_st_usages[res].in_use < 0]
1338 # Now, let's check the quotas
1339 # NOTE(Vek): We're only concerned about positive increments.
1340 # If a project has gone over quota, we want them to
1341 # be able to reduce their usage without any
1342 # problems.
1343 for key, value in user_or_st_usages.items():
1344 if key not in project_usages: 1344 ↛ 1343line 1344 didn't jump to line 1343 because the condition on line 1344 was always true
1345 project_usages[key] = value
1346 overs = [res for res, delta in deltas.items()
1347 if user_or_st_quotas[res] >= 0 and delta >= 0 and
1348 (0 <= project_quotas[res] < delta +
1349 project_usages[res]['total'] or
1350 user_or_st_quotas[res] < delta +
1351 user_or_st_usages[res].total)]
1353 # NOTE(carloss): If OverQuota is allowed, there is no problem to exceed
1354 # the quotas, so we reset the overs list and LOG it.
1355 if overs and overquota_allowed: 1355 ↛ 1356line 1355 didn't jump to line 1356 because the condition on line 1355 was never true
1356 msg = _("The service has identified one or more exceeded "
1357 "quotas. Please check the quotas for project "
1358 "%(project_id)s, user %(user_id)s and share type "
1359 "%(share_type_id)s, and adjust them if "
1360 "necessary.") % {
1361 "project_id": project_id,
1362 "user_id": user_id,
1363 "share_type_id": share_type_id
1364 }
1365 LOG.warning(msg)
1366 overs = []
1368 # NOTE(Vek): The quota check needs to be in the transaction,
1369 # but the transaction doesn't fail just because
1370 # we're over quota, so the OverQuota raise is
1371 # outside the transaction. If we did the raise
1372 # here, our usage updates would be discarded, but
1373 # they're not invalidated by being over-quota.
1375 # Create the reservations
1376 if not overs: 1376 ↛ 1406line 1376 didn't jump to line 1406 because the condition on line 1376 was always true
1377 reservations = []
1378 for res, delta in deltas.items():
1379 reservation = _reservation_create(
1380 elevated,
1381 uuidutils.generate_uuid(),
1382 user_or_st_usages[res],
1383 project_id,
1384 user_id,
1385 res, delta, expire,
1386 share_type_id=share_type_id,
1387 )
1388 reservations.append(reservation.uuid)
1390 # Also update the reserved quantity
1391 # NOTE(Vek): Again, we are only concerned here about
1392 # positive increments. Here, though, we're
1393 # worried about the following scenario:
1394 #
1395 # 1) User initiates resize down.
1396 # 2) User allocates a new instance.
1397 # 3) Resize down fails or is reverted.
1398 # 4) User is now over quota.
1399 #
1400 # To prevent this, we only update the
1401 # reserved value if the delta is positive.
1402 if delta > 0:
1403 user_or_st_usages[res].reserved += delta
1405 # Apply updates to the usages table
1406 for usage_ref in user_or_st_usages.values():
1407 context.session.add(usage_ref)
1409 # NOTE(stephenfin): commit changes before we raise any exceptions
1411 context.session.commit()
1412 context.session.begin()
1414 if unders:
1415 LOG.warning("Change will make usage less than 0 for the following "
1416 "resources: %s", unders)
1417 if overs: 1417 ↛ 1418line 1417 didn't jump to line 1418 because the condition on line 1417 was never true
1418 if project_quotas == user_or_st_quotas:
1419 usages = project_usages
1420 else:
1421 usages = user_or_st_usages
1422 usages = {k: dict(in_use=v['in_use'], reserved=v['reserved'])
1423 for k, v in usages.items()}
1424 raise exception.OverQuota(
1425 overs=sorted(overs), quotas=user_or_st_quotas, usages=usages)
1427 return reservations
1430def _quota_reservations_query(context, reservations):
1431 """Return the relevant reservations."""
1432 return model_query(
1433 context, models.Reservation,
1434 read_deleted="no",
1435 ).filter(
1436 models.Reservation.uuid.in_(reservations),
1437 ).with_for_update()
1440@require_context
1441@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
1442@context_manager.writer.independent
1443def reservation_commit(context, reservations, project_id=None, user_id=None,
1444 share_type_id=None):
1445 if share_type_id:
1446 st_usages = _get_share_type_quota_usages(
1447 context, project_id, share_type_id,
1448 )
1449 else:
1450 st_usages = {}
1451 user_usages = _get_user_quota_usages(context, project_id, user_id)
1453 reservation_query = _quota_reservations_query(context, reservations)
1454 for reservation in reservation_query.all():
1455 if reservation['share_type_id']:
1456 usages = st_usages
1457 else:
1458 usages = user_usages
1459 usage = usages[reservation.resource]
1460 if reservation.delta >= 0:
1461 usage.reserved -= reservation.delta
1462 usage.in_use += reservation.delta
1463 reservation_query.soft_delete(synchronize_session=False)
1466@require_context
1467@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
1468@context_manager.writer.independent
1469def reservation_rollback(context, reservations, project_id=None, user_id=None,
1470 share_type_id=None):
1471 if share_type_id: 1471 ↛ 1472line 1471 didn't jump to line 1472 because the condition on line 1471 was never true
1472 st_usages = _get_share_type_quota_usages(
1473 context, project_id, share_type_id,
1474 )
1475 else:
1476 st_usages = {}
1477 user_usages = _get_user_quota_usages(context, project_id, user_id)
1479 reservation_query = _quota_reservations_query(context, reservations)
1480 for reservation in reservation_query.all():
1481 if reservation['share_type_id']: 1481 ↛ 1482line 1481 didn't jump to line 1482 because the condition on line 1481 was never true
1482 usages = st_usages
1483 else:
1484 usages = user_usages
1485 usage = usages[reservation.resource]
1486 if reservation.delta >= 0: 1486 ↛ 1480line 1486 didn't jump to line 1480 because the condition on line 1486 was always true
1487 usage.reserved -= reservation.delta
1488 reservation_query.soft_delete(synchronize_session=False)
1491@require_admin_context
1492@context_manager.writer.independent
1493def quota_destroy_all_by_project_and_user(context, project_id, user_id):
1494 model_query(
1495 context, models.ProjectUserQuota, read_deleted="no",
1496 ).filter_by(
1497 project_id=project_id,
1498 ).filter_by(user_id=user_id).soft_delete(synchronize_session=False)
1500 model_query(
1501 context, models.QuotaUsage, read_deleted="no",
1502 ).filter_by(
1503 project_id=project_id,
1504 ).filter_by(user_id=user_id).soft_delete(synchronize_session=False)
1506 model_query(
1507 context, models.Reservation, read_deleted="no",
1508 ).filter_by(
1509 project_id=project_id,
1510 ).filter_by(user_id=user_id).soft_delete(synchronize_session=False)
1513@require_admin_context
1514@context_manager.writer.independent
1515def quota_destroy_all_by_share_type(context, share_type_id, project_id=None):
1516 return _quota_destroy_all_by_share_type(
1517 context, share_type_id, project_id=project_id,
1518 )
1521@require_admin_context
1522def _quota_destroy_all_by_share_type(context, share_type_id, project_id=None):
1523 """Soft deletes all quotas, usages and reservations.
1525 :param context: request context for queries, updates and logging
1526 :param share_type_id: ID of the share type to filter the quotas, usages
1527 and reservations under.
1528 :param project_id: ID of the project to filter the quotas, usages and
1529 reservations under. If not provided, share type quotas for all
1530 projects will be acted upon.
1531 """
1532 share_type_quotas = model_query(
1533 context, models.ProjectShareTypeQuota,
1534 read_deleted="no",
1535 ).filter_by(share_type_id=share_type_id)
1537 share_type_quota_usages = model_query(
1538 context, models.QuotaUsage, read_deleted="no",
1539 ).filter_by(share_type_id=share_type_id)
1541 share_type_quota_reservations = model_query(
1542 context, models.Reservation, read_deleted="no",
1543 ).filter_by(share_type_id=share_type_id)
1545 if project_id is not None: 1545 ↛ 1546line 1545 didn't jump to line 1546 because the condition on line 1545 was never true
1546 share_type_quotas = share_type_quotas.filter_by(
1547 project_id=project_id,
1548 )
1549 share_type_quota_usages = share_type_quota_usages.filter_by(
1550 project_id=project_id,
1551 )
1552 share_type_quota_reservations = (
1553 share_type_quota_reservations.filter_by(project_id=project_id)
1554 )
1556 share_type_quotas.soft_delete(synchronize_session=False)
1557 share_type_quota_usages.soft_delete(synchronize_session=False)
1558 share_type_quota_reservations.soft_delete(synchronize_session=False)
1561@require_admin_context
1562@context_manager.writer.independent
1563def quota_destroy_all_by_project(context, project_id):
1564 model_query(
1565 context, models.Quota, read_deleted="no",
1566 ).filter_by(
1567 project_id=project_id,
1568 ).soft_delete(synchronize_session=False)
1570 model_query(
1571 context, models.ProjectUserQuota, read_deleted="no",
1572 ).filter_by(
1573 project_id=project_id,
1574 ).soft_delete(synchronize_session=False)
1576 model_query(
1577 context, models.QuotaUsage, read_deleted="no",
1578 ).filter_by(
1579 project_id=project_id,
1580 ).soft_delete(synchronize_session=False)
1582 model_query(
1583 context, models.Reservation, read_deleted="no",
1584 ).filter_by(
1585 project_id=project_id,
1586 ).soft_delete(synchronize_session=False)
1589@require_admin_context
1590@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
1591@context_manager.writer
1592def reservation_expire(context):
1593 current_time = timeutils.utcnow()
1594 reservation_query = model_query(
1595 context, models.Reservation,
1596 read_deleted="no"
1597 ).filter(models.Reservation.expire < current_time)
1599 for reservation in reservation_query.all():
1600 if reservation.delta >= 0: 1600 ↛ 1599line 1600 didn't jump to line 1599 because the condition on line 1600 was always true
1601 quota_usage = model_query(
1602 context, models.QuotaUsage, read_deleted="no",
1603 ).filter(
1604 models.QuotaUsage.id == reservation.usage_id,
1605 ).first()
1606 quota_usage.reserved -= reservation.delta
1607 context.session.add(quota_usage)
1609 reservation_query.soft_delete(synchronize_session=False)
1612################
1614def _extract_subdict_by_fields(source_dict, fields):
1615 dict_to_extract_from = copy.deepcopy(source_dict)
1616 sub_dict = {}
1617 for field in fields:
1618 field_value = dict_to_extract_from.pop(field, None)
1619 if field_value:
1620 sub_dict.update({field: field_value})
1622 return sub_dict, dict_to_extract_from
1625def _extract_share_instance_values(values):
1626 share_instance_model_fields = [
1627 'status', 'host', 'scheduled_at', 'launched_at', 'terminated_at',
1628 'share_server_id', 'share_network_id', 'availability_zone_id',
1629 'replica_state', 'share_type_id', 'share_type', 'access_rules_status',
1630 'mount_point_name', 'encryption_key_ref',
1631 ]
1632 share_instance_values, share_values = (
1633 _extract_subdict_by_fields(values, share_instance_model_fields)
1634 )
1635 return share_instance_values, share_values
1638def _change_size_to_instance_size(snap_instance_values):
1639 if 'size' in snap_instance_values:
1640 snap_instance_values['instance_size'] = snap_instance_values['size']
1641 snap_instance_values.pop('size')
1644def _extract_snapshot_instance_values(values):
1645 fields = ['status', 'progress', 'provider_location']
1646 snapshot_instance_values, snapshot_values = (
1647 _extract_subdict_by_fields(values, fields)
1648 )
1649 return snapshot_instance_values, snapshot_values
1652################
1655@require_context
1656@context_manager.writer
1657def share_instance_create(context, share_id, values):
1658 return _share_instance_create(context, share_id, values)
1661def _share_instance_create(context, share_id, values):
1662 if not values.get('id'):
1663 values['id'] = uuidutils.generate_uuid()
1664 values.update({'share_id': share_id})
1666 share_instance_ref = models.ShareInstance()
1667 share_instance_ref.update(values)
1668 share_instance_ref.save(session=context.session)
1670 return _share_instance_get(context, share_instance_ref['id'])
1673@require_context
1674@require_availability_zone_exists(strict=False)
1675@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
1676@context_manager.writer
1677def share_instance_update(context, share_instance_id, values,
1678 with_share_data=False):
1679 instance_ref = _share_instance_update(
1680 context, share_instance_id, values,
1681 )
1682 if with_share_data:
1683 parent_share = _share_get(context, instance_ref['share_id'])
1684 instance_ref.set_share_data(parent_share)
1686 return instance_ref
1689def _share_instance_update(
1690 context, share_instance_id, values, with_share_data=False
1691):
1692 share_instance_ref = _share_instance_get(
1693 context, share_instance_id,
1694 with_share_data=with_share_data)
1695 share_instance_ref.update(values)
1696 share_instance_ref.save(session=context.session)
1697 return share_instance_ref
1700@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
1701@context_manager.writer
1702def share_and_snapshot_instances_status_update(
1703 context, values, share_instance_ids=None, snapshot_instance_ids=None,
1704 current_expected_status=None,
1705):
1706 updated_share_instances = None
1707 updated_snapshot_instances = None
1709 if current_expected_status and share_instance_ids:
1710 filters = {'instance_ids': share_instance_ids}
1711 share_instances = _share_instance_get_all(context, filters=filters)
1712 all_instances_are_compliant = all(
1713 instance['status'] == current_expected_status
1714 for instance in share_instances)
1716 if not all_instances_are_compliant:
1717 msg = _('At least one of the shares is not in the %(status)s '
1718 'status.') % {
1719 'status': current_expected_status
1720 }
1721 raise exception.InvalidShareInstance(reason=msg)
1723 if current_expected_status and snapshot_instance_ids:
1724 filters = {'instance_ids': snapshot_instance_ids}
1725 snapshot_instances = _share_snapshot_instance_get_all_with_filters(
1726 context, filters,
1727 )
1728 all_snap_instances_are_compliant = all(
1729 snap_instance['status'] == current_expected_status
1730 for snap_instance in snapshot_instances)
1731 if not all_snap_instances_are_compliant: 1731 ↛ 1738line 1731 didn't jump to line 1738 because the condition on line 1731 was always true
1732 msg = _('At least one of the snapshots is not in the '
1733 '%(status)s status.') % {
1734 'status': current_expected_status
1735 }
1736 raise exception.InvalidShareSnapshotInstance(reason=msg)
1738 if share_instance_ids: 1738 ↛ 1743line 1738 didn't jump to line 1743 because the condition on line 1738 was always true
1739 updated_share_instances = _share_instance_status_update(
1740 context, share_instance_ids, values,
1741 )
1743 if snapshot_instance_ids: 1743 ↛ 1748line 1743 didn't jump to line 1748 because the condition on line 1743 was always true
1744 updated_snapshot_instances = _share_snapshot_instances_status_update(
1745 context, snapshot_instance_ids, values,
1746 )
1748 return updated_share_instances, updated_snapshot_instances
1751@require_context
1752@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
1753@context_manager.writer
1754def share_instance_status_update(context, share_instance_ids, values):
1755 return _share_instance_status_update(context, share_instance_ids, values)
1758def _share_instance_status_update(context, share_instance_ids, values):
1759 result = model_query(
1760 context, models.ShareInstance, read_deleted="no",
1761 ).filter(
1762 models.ShareInstance.id.in_(share_instance_ids)
1763 ).update(
1764 values, synchronize_session=False,
1765 )
1766 return result
1769@require_context
1770@context_manager.reader
1771def share_instance_get(context, share_instance_id, with_share_data=False):
1772 return _share_instance_get(
1773 context, share_instance_id,
1774 with_share_data=with_share_data,
1775 )
1778def _share_instance_get(context, share_instance_id, with_share_data=False):
1779 result = model_query(
1780 context, models.ShareInstance,
1781 ).filter_by(
1782 id=share_instance_id,
1783 ).options(
1784 orm.joinedload(
1785 models.ShareInstance.export_locations
1786 ).joinedload(models.ShareInstanceExportLocations._el_metadata_bare),
1787 orm.joinedload(models.ShareInstance.share_type),
1788 ).first()
1789 if result is None:
1790 raise exception.NotFound()
1792 if with_share_data:
1793 parent_share = _share_get(context, result['share_id'])
1794 result.set_share_data(parent_share)
1796 return result
1799@require_admin_context
1800@context_manager.reader
1801def share_instance_get_all(context, filters=None):
1802 return _share_instance_get_all(context, filters=filters)
1805@require_admin_context
1806def _share_instance_get_all(context, filters=None):
1807 query = model_query(
1808 context, models.ShareInstance, read_deleted="no",
1809 ).options(
1810 orm.joinedload(models.ShareInstance.export_locations),
1811 )
1813 filters = filters or {}
1815 export_location_id = filters.get('export_location_id')
1816 export_location_path = filters.get('export_location_path')
1817 if export_location_id or export_location_path:
1818 query = query.join(
1819 models.ShareInstanceExportLocations,
1820 models.ShareInstanceExportLocations.share_instance_id ==
1821 models.ShareInstance.id)
1822 if export_location_path:
1823 query = query.filter(
1824 models.ShareInstanceExportLocations.path ==
1825 export_location_path)
1826 if export_location_id:
1827 query = query.filter(
1828 models.ShareInstanceExportLocations.uuid ==
1829 export_location_id)
1831 query = query.join(
1832 models.Share,
1833 models.Share.id == models.ShareInstance.share_id,
1834 )
1835 is_soft_deleted = filters.get('is_soft_deleted')
1836 if is_soft_deleted:
1837 query = query.filter(models.Share.is_soft_deleted == true())
1838 else:
1839 query = query.filter(models.Share.is_soft_deleted == false())
1841 instance_ids = filters.get('instance_ids')
1842 if instance_ids:
1843 query = query.filter(models.ShareInstance.id.in_(instance_ids))
1845 # TODO(gouthamr): This DB API method needs to be generalized for all
1846 # share instance fields.
1847 host = filters.get('host')
1848 if host:
1849 query = query.filter(
1850 or_(models.ShareInstance.host == host,
1851 models.ShareInstance.host.like("{0}#%".format(host)))
1852 )
1853 share_server_id = filters.get('share_server_id')
1854 if share_server_id: 1854 ↛ 1855line 1854 didn't jump to line 1855 because the condition on line 1854 was never true
1855 query = query.filter(
1856 models.ShareInstance.share_server_id == share_server_id)
1858 status = filters.get('status')
1859 if status:
1860 query = query.filter(models.ShareInstance.status == status)
1862 encryption_key_ref = filters.get('encryption_key_ref')
1863 if encryption_key_ref: 1863 ↛ 1864line 1863 didn't jump to line 1864 because the condition on line 1863 was never true
1864 query = query.filter(
1865 models.ShareInstance.encryption_key_ref == encryption_key_ref)
1867 # Returns list of share instances that satisfy filters.
1868 query = query.all()
1869 return query
1872@require_context
1873def _update_share_instance_usages(context, share, instance_ref,
1874 is_replica=False,
1875 deferred_delete=False):
1876 deltas = {}
1877 # if share is expected to be deferred_deleted, we drop its quotas
1878 # whether or not it has additional share instances
1879 no_instances_remain = deferred_delete or len(share.instances) == 0
1880 share_usages_to_release = {"shares": -1, "gigabytes": -share['size']}
1881 replica_usages_to_release = {"share_replicas": -1,
1882 "replica_gigabytes": -share['size']}
1884 if is_replica and no_instances_remain:
1885 # A share that had a replication_type is being deleted, so there's
1886 # need to update the share replica quotas and the share quotas
1887 deltas.update(replica_usages_to_release)
1888 deltas.update(share_usages_to_release)
1889 elif is_replica:
1890 # The user is deleting a share replica
1891 deltas.update(replica_usages_to_release)
1892 else:
1893 # A share with no replication_type is being deleted
1894 deltas.update(share_usages_to_release)
1896 reservations = None
1897 try:
1898 # we give the user_id of the share, to update
1899 # the quota usage for the user, who created the share
1900 reservations = QUOTAS.reserve(
1901 context,
1902 project_id=share['project_id'],
1903 user_id=share['user_id'],
1904 share_type_id=instance_ref['share_type_id'],
1905 **deltas)
1906 QUOTAS.commit(
1907 context, reservations, project_id=share['project_id'],
1908 user_id=share['user_id'],
1909 share_type_id=instance_ref['share_type_id'])
1910 except Exception:
1911 resource_name = (
1912 'share replica' if is_replica else 'share')
1913 resource_id = instance_ref['id'] if is_replica else share['id']
1914 msg = (_("Failed to update usages deleting %(resource_name)s "
1915 "'%(id)s'.") % {'id': resource_id,
1916 "resource_name": resource_name})
1917 LOG.exception(msg)
1918 if reservations:
1919 QUOTAS.rollback(
1920 context, reservations,
1921 share_type_id=instance_ref['share_type_id'])
1924@require_context
1925@context_manager.writer
1926def share_instance_delete(context, instance_id, need_to_update_usages=False):
1927 _share_instance_delete(
1928 context, instance_id,
1929 need_to_update_usages=need_to_update_usages,
1930 )
1933def _share_instance_delete(context, instance_id,
1934 need_to_update_usages=False):
1936 export_locations_update(context, instance_id, [], delete=True)
1937 instance_ref = _share_instance_get(context, instance_id)
1938 is_replica = instance_ref['replica_state'] is not None
1939 instance_ref.soft_delete(session=context.session, update_status=True)
1941 share = _share_get(context, instance_ref['share_id'])
1942 if len(share.instances) == 0:
1944 # NOTE(zzzeek) currently this potentially is required for current
1945 # tempest runs to pass
1946 with context_manager.writer.independent.using(context) as oob_session:
1947 oob_session.query(models.ShareAccessMapping).filter_by(
1948 share_id=share['id']
1949 ).soft_delete()
1951 context.session.query(models.ShareMetadata).filter_by(
1952 share_id=share['id'],
1953 ).soft_delete()
1954 share.soft_delete(session=context.session)
1956 if need_to_update_usages:
1957 _update_share_instance_usages(context, share, instance_ref,
1958 is_replica=is_replica,
1959 deferred_delete=False)
1962@require_context
1963@context_manager.writer
1964def update_share_instance_quota_usages(context, instance_id):
1965 # This method is specifically written for deferred deletion share
1966 # instance usage.
1967 instance_ref = _share_instance_get(context, instance_id)
1968 is_replica = instance_ref['replica_state'] is not None
1969 share = _share_get(context, instance_ref['share_id'])
1970 _update_share_instance_usages(context, share, instance_ref,
1971 is_replica=is_replica,
1972 deferred_delete=True)
1975def _set_instances_share_data(instances):
1976 instances = instances.options(
1977 orm.joinedload(models.ShareInstance.share)).all()
1978 instances = [s for s in instances if s.share]
1979 for s in instances:
1980 s.set_share_data(s.share)
1982 return instances
1985@require_admin_context
1986@context_manager.reader
1987def share_instance_get_all_by_host(context, host, with_share_data=False,
1988 status=None):
1989 """Retrieves all share instances hosted on a host."""
1990 instances = (
1991 model_query(context, models.ShareInstance).filter(
1992 or_(
1993 models.ShareInstance.host == host,
1994 models.ShareInstance.host.like("{0}#%".format(host))
1995 )
1996 )
1997 )
1998 if status is not None:
1999 instances = instances.filter(models.ShareInstance.status == status)
2000 if with_share_data:
2001 instances = _set_instances_share_data(instances)
2002 else:
2003 # Returns list of all instances that satisfy filters.
2004 instances = instances.all()
2005 return instances
2008@require_context
2009@context_manager.reader
2010def share_instance_sizes_sum_by_host(context, host):
2011 result = model_query(
2012 context, models.Share, func.sum(models.Share.size),
2013 ).join(
2014 models.ShareInstance.share,
2015 ).filter(or_(
2016 models.ShareInstance.host == host,
2017 models.ShareInstance.host.like("{0}#%".format(host)),
2018 )).first()
2019 return int(result[0] or 0)
2022@require_context
2023@context_manager.reader
2024def share_instance_get_all_by_share_network(context, share_network_id):
2025 """Returns list of share instances that belong to given share network."""
2026 result = (
2027 model_query(context, models.ShareInstance).filter(
2028 models.ShareInstance.share_network_id == share_network_id,
2029 ).all()
2030 )
2031 return result
2034@require_context
2035@context_manager.reader
2036def share_instance_get_all_by_share_server(context, share_server_id,
2037 with_share_data=False):
2038 """Returns list of share instance with given share server."""
2039 result = (
2040 model_query(context, models.ShareInstance).filter(
2041 models.ShareInstance.share_server_id == share_server_id,
2042 )
2043 )
2045 if with_share_data: 2045 ↛ 2046line 2045 didn't jump to line 2046 because the condition on line 2045 was never true
2046 result = _set_instances_share_data(result)
2047 else:
2048 result = result.all()
2050 return result
2053@require_context
2054@context_manager.reader
2055def share_instance_get_all_by_share(context, share_id):
2056 """Returns list of share instances that belong to given share."""
2057 result = (
2058 model_query(context, models.ShareInstance).filter(
2059 models.ShareInstance.share_id == share_id,
2060 ).all()
2061 )
2062 return result
2065@require_context
2066@context_manager.reader
2067def share_instance_get_all_by_share_group_id(context, share_group_id):
2068 """Returns list of share instances that belong to given share group."""
2069 result = (
2070 model_query(context, models.Share).filter(
2071 models.Share.share_group_id == share_group_id,
2072 ).all()
2073 )
2074 instances = []
2075 for share in result:
2076 instance = share.instance
2077 instance.set_share_data(share)
2078 instances.append(instance)
2080 return instances
2083################
2085def _share_replica_get_with_filters(context, share_id=None, replica_id=None,
2086 replica_state=None, status=None,
2087 with_share_server=True):
2089 query = model_query(context, models.ShareInstance, read_deleted="no")
2091 if not context.is_admin:
2092 query = query.join(
2093 models.Share,
2094 models.ShareInstance.share_id == models.Share.id
2095 ).filter(
2096 models.Share.project_id == context.project_id,
2097 )
2099 if share_id is not None:
2100 query = query.filter(models.ShareInstance.share_id == share_id)
2102 if replica_id is not None:
2103 query = query.filter(models.ShareInstance.id == replica_id)
2105 if replica_state is not None:
2106 query = query.filter(
2107 models.ShareInstance.replica_state == replica_state)
2108 else:
2109 query = query.filter(models.ShareInstance.replica_state.isnot(None))
2111 if status is not None:
2112 query = query.filter(models.ShareInstance.status == status)
2114 if with_share_server:
2115 query = query.options(
2116 orm.joinedload(models.ShareInstance.share_server),
2117 )
2119 return query
2122@require_context
2123@context_manager.reader
2124def share_replicas_get_all(context, with_share_data=False,
2125 with_share_server=True):
2126 """Returns replica instances for all available replicated shares."""
2127 result = _share_replica_get_with_filters(
2128 context, with_share_server=with_share_server,
2129 )
2131 if with_share_data:
2132 result = _set_instances_share_data(result)
2133 else:
2134 result = result.all()
2136 return result
2139@require_context
2140@context_manager.reader
2141def share_replicas_get_all_by_share(context, share_id,
2142 with_share_data=False,
2143 with_share_server=False,):
2144 """Returns replica instances for a given share."""
2145 result = _share_replica_get_with_filters(
2146 context, with_share_server=with_share_server,
2147 share_id=share_id,
2148 )
2150 if with_share_data:
2151 result = _set_instances_share_data(result)
2152 else:
2153 result = result.all()
2155 return result
2158@require_context
2159@context_manager.reader
2160def share_replicas_get_available_active_replica(context, share_id,
2161 with_share_data=False,
2162 with_share_server=False):
2163 """Returns an 'active' replica instance that is 'available'."""
2164 result = _share_replica_get_with_filters(
2165 context, with_share_server=with_share_server, share_id=share_id,
2166 replica_state=constants.REPLICA_STATE_ACTIVE,
2167 status=constants.STATUS_AVAILABLE,
2168 )
2170 if result.first() and with_share_data:
2171 result = _set_instances_share_data(result)[0]
2172 else:
2173 result = result.first()
2175 return result
2178@require_context
2179@context_manager.reader
2180def share_replica_get(context, replica_id, with_share_data=False,
2181 with_share_server=False):
2182 """Returns summary of requested replica if available."""
2183 result = _share_replica_get_with_filters(
2184 context,
2185 with_share_server=with_share_server,
2186 replica_id=replica_id,
2187 )
2189 if result.first() and with_share_data:
2190 result = _set_instances_share_data(result)[0]
2191 else:
2192 result = result.first()
2194 if result is None:
2195 raise exception.ShareReplicaNotFound(replica_id=replica_id)
2197 return result
2200@require_context
2201@require_availability_zone_exists(strict=False)
2202@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
2203@context_manager.writer
2204def share_replica_update(context, share_replica_id, values,
2205 with_share_data=False):
2206 """Updates a share replica with specified values."""
2207 updated_share_replica = _share_instance_update(
2208 context, share_replica_id, values,
2209 with_share_data=with_share_data
2210 )
2212 return updated_share_replica
2215@require_context
2216@context_manager.writer
2217def share_replica_delete(
2218 context, replica_id, need_to_update_usages=True,
2219):
2220 """Deletes a share replica."""
2222 _share_instance_delete(
2223 context, replica_id,
2224 need_to_update_usages=need_to_update_usages,
2225 )
2228################
2231def _process_share_filters(query, filters, project_id=None, is_public=False):
2232 if filters is None: 2232 ↛ 2233line 2232 didn't jump to line 2233 because the condition on line 2232 was never true
2233 filters = {}
2235 share_filter_keys = ['share_group_id', 'snapshot_id',
2236 'is_soft_deleted', 'source_backup_id']
2237 instance_filter_keys = ['share_server_id', 'status', 'share_type_id',
2238 'host', 'share_network_id', 'mount_point_name',
2239 'encryption_key_ref']
2240 share_filters = {}
2241 instance_filters = {}
2243 for k, v in filters.items():
2244 share_filters.update({k: v}) if k in share_filter_keys else None
2245 instance_filters.update({k: v}) if k in instance_filter_keys else None
2247 no_key = 'key_is_absent'
2249 def _filter_data(query, model, desired_filters):
2250 for key, value in desired_filters.items():
2251 filter_attr = getattr(model, key, no_key)
2252 if filter_attr == no_key: 2252 ↛ 2253line 2252 didn't jump to line 2253 because the condition on line 2252 was never true
2253 pass
2254 query = query.filter(filter_attr == value)
2255 return query
2257 if share_filters: 2257 ↛ 2259line 2257 didn't jump to line 2259 because the condition on line 2257 was always true
2258 query = _filter_data(query, models.Share, share_filters)
2259 if instance_filters:
2260 query = _filter_data(query, models.ShareInstance, instance_filters)
2262 if project_id:
2263 if is_public: 2263 ↛ 2264line 2263 didn't jump to line 2264 because the condition on line 2263 was never true
2264 query = query.filter(or_(models.Share.project_id == project_id,
2265 models.Share.is_public))
2266 else:
2267 query = query.filter(models.Share.project_id == project_id)
2269 safe_regex_filter, db_regexp_op = _get_regexp_ops(CONF.database.connection)
2270 display_name = filters.get('display_name')
2271 if display_name:
2272 query = query.filter(
2273 models.Share.display_name == display_name)
2274 else:
2275 display_name = filters.get('display_name~')
2276 if display_name:
2277 query = query.filter(
2278 models.Share.display_name.op(db_regexp_op)(
2279 _get_filter_value_by_op(
2280 db_regexp_op, display_name, safe_regex_filter)))
2282 display_description = filters.get('display_description')
2283 if display_description:
2284 query = query.filter(
2285 models.Share.display_description == display_description)
2286 else:
2287 display_description = filters.get('display_description~')
2288 if display_description:
2289 query = query.filter(
2290 models.Share.display_description.op(db_regexp_op)(
2291 _get_filter_value_by_op(
2292 db_regexp_op, display_description, safe_regex_filter)))
2294 export_location_id = filters.pop('export_location_id', None)
2295 export_location_path = filters.pop('export_location_path', None)
2296 if export_location_id or export_location_path:
2297 query = query.join(
2298 models.ShareInstanceExportLocations,
2299 models.ShareInstanceExportLocations.share_instance_id ==
2300 models.ShareInstance.id,
2301 )
2302 if export_location_path:
2303 query = query.filter(
2304 models.ShareInstanceExportLocations.path ==
2305 export_location_path)
2306 if export_location_id:
2307 query = query.filter(
2308 models.ShareInstanceExportLocations.uuid ==
2309 export_location_id)
2311 if 'metadata' in filters: 2311 ↛ 2312line 2311 didn't jump to line 2312 because the condition on line 2311 was never true
2312 for k, v in filters['metadata'].items():
2313 # pylint: disable=no-member
2314 query = query.filter(
2315 or_(models.Share.share_metadata.any(
2316 key=k, value=v)))
2317 if 'extra_specs' in filters: 2317 ↛ 2318line 2317 didn't jump to line 2318 because the condition on line 2317 was never true
2318 query = query.join(
2319 models.ShareTypeExtraSpecs,
2320 models.ShareTypeExtraSpecs.share_type_id ==
2321 models.ShareInstance.share_type_id,
2322 )
2323 for k, v in filters['extra_specs'].items():
2324 query = query.filter(and_(models.ShareTypeExtraSpecs.key == k,
2325 models.ShareTypeExtraSpecs.value == v))
2327 if not filters.get('list_deferred_delete'):
2328 query = query.filter(and_(
2329 models.ShareInstance.status != (
2330 constants.STATUS_DEFERRED_DELETING),
2331 models.ShareInstance.status != (
2332 constants.STATUS_ERROR_DEFERRED_DELETING)))
2333 return query
2336def _get_filter_value_by_op(op, filter_value, safe_regex_filter):
2337 if op == 'LIKE': 2337 ↛ 2338line 2337 didn't jump to line 2338 because the condition on line 2337 was never true
2338 return u'%' + filter_value + u'%'
2339 else:
2340 return safe_regex_filter(filter_value)
2343def _safe_regex_mysql(raw_string):
2344 """Make regex safe to mysql.
2346 Certain items like '|' are interpreted raw by mysql REGEX. If you
2347 search for a single | then you trigger an error because it's
2348 expecting content on either side.
2350 For consistency sake we escape all '|'. This does mean we wouldn't
2351 support something like foo|bar to match completely different
2352 things, however, one can argue putting such complicated regex into
2353 name search probably means you are doing this wrong.
2354 """
2355 return raw_string.replace('|', '\\|')
2358def _get_regexp_ops(connection):
2359 """Return safety filter and db opts for regex."""
2360 regexp_op_map = {
2361 'postgresql': '~',
2362 'mysql': 'REGEXP',
2363 'sqlite': 'REGEXP'
2364 }
2365 regex_safe_filters = {
2366 'mysql': _safe_regex_mysql
2367 }
2368 db_type = _db_connection_type(connection)
2370 return (regex_safe_filters.get(db_type, lambda x: x),
2371 regexp_op_map.get(db_type, 'LIKE'))
2374def _db_connection_type(db_connection):
2375 """Returns a lowercase symbol for the db type.
2377 This is useful when we need to change what we are doing per DB
2378 (like handling regexes). In a CellsV2 world it probably needs to
2379 do something better than use the database configuration string.
2380 """
2382 db_string = db_connection.split(':')[0].split('+')[0]
2383 return db_string.lower()
2386def _metadata_refs(metadata_dict, meta_class):
2387 metadata_refs = []
2388 if metadata_dict:
2389 for k, v in metadata_dict.items():
2390 value = str(v) if isinstance(v, bool) else v
2392 metadata_ref = meta_class()
2393 metadata_ref['key'] = k
2394 metadata_ref['value'] = value
2395 metadata_refs.append(metadata_ref)
2396 return metadata_refs
2399@require_context
2400@require_availability_zone_exists(strict=False)
2401@context_manager.writer
2402def share_create(context, share_values, create_share_instance=True):
2403 values = copy.deepcopy(share_values)
2404 values = ensure_model_dict_has_id(values)
2405 values['share_metadata'] = _metadata_refs(values.get('metadata'),
2406 models.ShareMetadata)
2407 share_ref = models.Share()
2408 share_instance_values, share_values = _extract_share_instance_values(
2409 values)
2410 share_ref.update(share_values)
2411 share_ref.save(session=context.session)
2413 if create_share_instance:
2414 _share_instance_create(context, share_ref['id'],
2415 share_instance_values)
2417 # NOTE(u_glide): Do so to prevent errors with relationships
2418 return _share_get(context, share_ref['id'])
2421@require_admin_context
2422def _share_data_get_for_project(
2423 context, project_id, user_id, share_type_id=None,
2424):
2425 query = model_query(
2426 context, models.Share,
2427 func.count(models.Share.id),
2428 func.sum(models.Share.size),
2429 read_deleted="no",
2430 ).filter_by(project_id=project_id)
2431 if share_type_id:
2432 query = query.join(
2433 models.Share.instances,
2434 ).filter_by(share_type_id=share_type_id)
2435 elif user_id: 2435 ↛ 2437line 2435 didn't jump to line 2437 because the condition on line 2435 was always true
2436 query = query.filter_by(user_id=user_id)
2437 result = query.first()
2438 return (result[0] or 0, result[1] or 0)
2441@require_context
2442@require_availability_zone_exists(strict=False)
2443@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
2444@context_manager.writer
2445def share_update(context, share_id, update_values):
2446 return _share_update(context, share_id, update_values)
2449@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
2450def _share_update(context, share_id, update_values):
2451 values = copy.deepcopy(update_values)
2453 share_instance_values, share_values = _extract_share_instance_values(
2454 values)
2456 share_ref = _share_get(context, share_id)
2458 _share_instance_update(
2459 context, share_ref.instance['id'], share_instance_values,
2460 )
2462 share_ref.update(share_values)
2463 share_ref.save(session=context.session)
2464 return share_ref
2467@require_context
2468@context_manager.reader
2469def share_get(context, share_id, **kwargs):
2470 return _share_get(context, share_id, **kwargs)
2473def _share_get(context, share_id, **kwargs):
2474 result = model_query(
2475 context, models.Share, **kwargs,
2476 ).options(
2477 orm.joinedload(models.Share.share_metadata),
2478 ).filter_by(id=share_id).first()
2480 if result is None:
2481 raise exception.NotFound()
2483 return result
2486def _share_get_all_with_filters(context, project_id=None, share_server_id=None,
2487 share_group_id=None, filters=None,
2488 is_public=False, sort_key=None,
2489 sort_dir=None, show_count=False):
2490 """Returns sorted list of shares that satisfies filters.
2492 :param context: context to query under
2493 :param project_id: project id that owns shares
2494 :param share_server_id: share server that hosts shares
2495 :param filters: dict of filters to specify share selection
2496 :param is_public: public shares from other projects will be added
2497 to result if True
2498 :param sort_key: key of models.Share to be used for sorting
2499 :param sort_dir: desired direction of sorting, can be 'asc' and 'desc'
2500 :returns: list -- models.Share
2501 :raises: exception.InvalidInput
2502 """
2503 if filters is None:
2504 filters = {}
2506 if not sort_key:
2507 sort_key = 'created_at'
2508 if not sort_dir:
2509 sort_dir = 'desc'
2511 query = model_query(
2512 context, models.Share,
2513 ).options(
2514 orm.joinedload(models.Share.share_metadata),
2515 ).join(
2516 models.ShareInstance,
2517 models.ShareInstance.share_id == models.Share.id
2518 )
2520 if share_group_id:
2521 filters['share_group_id'] = share_group_id
2522 if share_server_id:
2523 filters['share_server_id'] = share_server_id
2525 # if not specified is_soft_deleted filter, default is False, to get
2526 # shares not in recycle bin.
2527 if 'is_soft_deleted' not in filters:
2528 filters['is_soft_deleted'] = False
2530 query = _process_share_filters(
2531 query, filters, project_id, is_public=is_public)
2533 try:
2534 query = apply_sorting(models.Share, query, sort_key, sort_dir)
2535 except AttributeError:
2536 try:
2537 query = apply_sorting(
2538 models.ShareInstance, query, sort_key, sort_dir)
2539 except AttributeError:
2540 msg = _("Wrong sorting key provided - '%s'.") % sort_key
2541 raise exception.InvalidInput(reason=msg)
2543 count = None
2544 # NOTE(carloss): Count must be calculated before limit and offset are
2545 # applied into the query.
2546 if show_count:
2547 count = query.order_by(models.Share.id).distinct().count()
2549 if 'limit' in filters:
2550 offset = filters.get('offset', 0)
2551 query = query.limit(filters['limit']).offset(offset)
2553 # Returns list of shares that satisfy filters.
2554 query = query.all()
2556 if show_count:
2557 return count, query
2559 return query
2562@require_admin_context
2563@context_manager.reader
2564def share_get_all_expired(context):
2565 query = model_query(
2566 context, models.Share,
2567 ).options(
2568 orm.joinedload(models.Share.share_metadata),
2569 ).join(
2570 models.ShareInstance,
2571 models.ShareInstance.share_id == models.Share.id,
2572 )
2573 filters = {"is_soft_deleted": True}
2574 query = _process_share_filters(query, filters=filters)
2575 scheduled_deleted_attr = getattr(models.Share,
2576 'scheduled_to_be_deleted_at', None)
2577 now_time = timeutils.utcnow()
2578 query = query.filter(scheduled_deleted_attr.op('<=')(now_time))
2579 result = query.all()
2581 return result
2584@require_admin_context
2585@context_manager.reader
2586def share_get_all(context, filters=None, sort_key=None, sort_dir=None):
2587 project_id = filters.pop('project_id', None) if filters else None
2588 query = _share_get_all_with_filters(
2589 context,
2590 project_id=project_id,
2591 filters=filters, sort_key=sort_key, sort_dir=sort_dir)
2592 return query
2595@require_admin_context
2596@context_manager.reader
2597def share_get_all_with_count(context, filters=None, sort_key=None,
2598 sort_dir=None):
2599 count, query = _share_get_all_with_filters(
2600 context,
2601 filters=filters, sort_key=sort_key, sort_dir=sort_dir,
2602 show_count=True)
2603 return count, query
2606@require_context
2607@context_manager.reader
2608def share_get_all_by_project(context, project_id, filters=None,
2609 is_public=False, sort_key=None, sort_dir=None):
2610 """Returns list of shares with given project ID."""
2611 query = _share_get_all_with_filters(
2612 context, project_id=project_id, filters=filters, is_public=is_public,
2613 sort_key=sort_key, sort_dir=sort_dir)
2614 return query
2617@require_context
2618@context_manager.reader
2619def share_get_all_by_project_with_count(
2620 context, project_id, filters=None, is_public=False, sort_key=None,
2621 sort_dir=None):
2622 """Returns list of shares with given project ID."""
2623 count, query = _share_get_all_with_filters(
2624 context, project_id=project_id, filters=filters, is_public=is_public,
2625 sort_key=sort_key, sort_dir=sort_dir, show_count=True)
2626 return count, query
2629@require_context
2630@context_manager.reader
2631def share_get_all_by_share_group_id(context, share_group_id,
2632 filters=None, sort_key=None,
2633 sort_dir=None):
2634 """Returns list of shares with given group ID."""
2635 query = _share_get_all_with_filters(
2636 context, share_group_id=share_group_id,
2637 filters=filters, sort_key=sort_key, sort_dir=sort_dir)
2638 return query
2641@require_context
2642@context_manager.reader
2643def share_get_all_by_share_group_id_with_count(context, share_group_id,
2644 filters=None, sort_key=None,
2645 sort_dir=None):
2646 """Returns list of shares with given share group ID."""
2647 count, query = _share_get_all_with_filters(
2648 context, share_group_id=share_group_id,
2649 filters=filters, sort_key=sort_key, sort_dir=sort_dir, show_count=True)
2650 return count, query
2653@require_context
2654@context_manager.reader
2655def share_get_all_by_share_server(context, share_server_id, filters=None,
2656 sort_key=None, sort_dir=None):
2657 """Returns list of shares with given share server."""
2658 query = _share_get_all_with_filters(
2659 context, share_server_id=share_server_id, filters=filters,
2660 sort_key=sort_key, sort_dir=sort_dir)
2661 return query
2664@require_context
2665@context_manager.reader
2666def share_get_all_soft_deleted(
2667 context, share_server_id, filters=None, sort_key=None, sort_dir=None):
2668 """Returns list of shares in recycle bin with given share server."""
2669 if filters is None: 2669 ↛ 2671line 2669 didn't jump to line 2671 because the condition on line 2669 was always true
2670 filters = {}
2671 filters["is_soft_deleted"] = True
2672 query = _share_get_all_with_filters(
2673 context, share_server_id=share_server_id, filters=filters,
2674 sort_key=sort_key, sort_dir=sort_dir)
2675 return query
2678@require_context
2679@context_manager.reader
2680def share_get_all_by_share_server_with_count(
2681 context, share_server_id, filters=None, sort_key=None, sort_dir=None):
2682 """Returns list of shares with given share server."""
2683 count, query = _share_get_all_with_filters(
2684 context, share_server_id=share_server_id, filters=filters,
2685 sort_key=sort_key, sort_dir=sort_dir, show_count=True)
2686 return count, query
2689@require_context
2690@context_manager.reader
2691def share_get_all_soft_deleted_by_network(
2692 context, share_network_id, filters=None, sort_key=None, sort_dir=None):
2693 """Returns list of shares in recycle bin with given share network."""
2694 if filters is None: 2694 ↛ 2696line 2694 didn't jump to line 2696 because the condition on line 2694 was always true
2695 filters = {}
2696 filters["share_network_id"] = share_network_id
2697 filters["is_soft_deleted"] = True
2698 query = _share_get_all_with_filters(context, filters=filters,
2699 sort_key=sort_key, sort_dir=sort_dir)
2700 return query
2703@require_context
2704@context_manager.writer
2705def share_delete(context, share_id):
2706 share_ref = _share_get(context, share_id)
2708 if len(share_ref.instances) > 0: 2708 ↛ 2709line 2708 didn't jump to line 2709 because the condition on line 2708 was never true
2709 msg = _("Share %(id)s has %(count)s share instances.") % {
2710 'id': share_id, 'count': len(share_ref.instances)}
2711 raise exception.InvalidShare(msg)
2713 share_ref.soft_delete(session=context.session)
2715 context.session.query(models.ShareMetadata).filter_by(
2716 share_id=share_id,
2717 ).soft_delete()
2720@require_context
2721@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
2722@context_manager.writer
2723def share_soft_delete(context, share_id):
2724 now_time = timeutils.utcnow()
2725 time_delta = datetime.timedelta(
2726 seconds=CONF.soft_deleted_share_retention_time)
2727 scheduled_to_be_deleted_at = now_time + time_delta
2728 update_values = {
2729 'is_soft_deleted': True,
2730 'scheduled_to_be_deleted_at': scheduled_to_be_deleted_at
2731 }
2733 share_ref = _share_get(context, share_id)
2734 share_ref.update(update_values)
2735 share_ref.save(session=context.session)
2738@require_context
2739@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
2740@context_manager.writer
2741def share_restore(context, share_id):
2742 update_values = {
2743 'is_soft_deleted': False,
2744 'scheduled_to_be_deleted_at': None
2745 }
2747 share_ref = _share_get(context, share_id)
2748 share_ref.update(update_values)
2749 share_ref.save(session=context.session)
2752###################
2755def _transfer_get(
2756 context, transfer_id, resource_type='share', read_deleted=False,
2757):
2758 """resource_type can be share or network(TODO network transfer)"""
2759 query = model_query(
2760 context, models.Transfer, read_deleted=read_deleted,
2761 ).filter_by(id=transfer_id)
2763 if not is_admin_context(context):
2764 if resource_type == 'share': 2764 ↛ 2771line 2764 didn't jump to line 2771 because the condition on line 2764 was always true
2765 share = models.Share
2766 query = query.filter(
2767 models.Transfer.resource_id == share.id,
2768 share.project_id == context.project_id,
2769 )
2771 result = query.first()
2772 if not result:
2773 raise exception.TransferNotFound(transfer_id=transfer_id)
2775 return result
2778@context_manager.reader
2779def transfer_get(context, transfer_id, read_deleted=False):
2780 return _transfer_get(context, transfer_id, read_deleted=read_deleted)
2783def _transfer_get_all(context, limit=None, sort_key=None,
2784 sort_dir=None, filters=None, offset=None):
2785 sort_key = sort_key or 'created_at'
2786 sort_dir = sort_dir or 'desc'
2788 query = model_query(context, models.Transfer)
2790 if filters:
2791 legal_filter_keys = (
2792 'display_name', 'display_name~',
2793 'id', 'resource_type', 'resource_id',
2794 'source_project_id', 'destination_project_id',
2795 )
2796 query = exact_filter(
2797 query, models.Transfer, filters, legal_filter_keys,
2798 )
2799 query = utils.paginate_query(
2800 query, models.Transfer, limit,
2801 sort_key=sort_key,
2802 sort_dir=sort_dir,
2803 offset=offset,
2804 )
2805 return query.all()
2808@require_admin_context
2809@context_manager.reader
2810def transfer_get_all(context, limit=None, sort_key=None,
2811 sort_dir=None, filters=None, offset=None):
2812 return _transfer_get_all(context, limit=limit,
2813 sort_key=sort_key, sort_dir=sort_dir,
2814 filters=filters, offset=offset)
2817@require_context
2818@context_manager.reader
2819def transfer_get_all_by_project(context, project_id,
2820 limit=None, sort_key=None,
2821 sort_dir=None, filters=None, offset=None):
2822 filters = filters.copy() if filters else {}
2823 filters['source_project_id'] = project_id
2824 return _transfer_get_all(context, limit=limit,
2825 sort_key=sort_key, sort_dir=sort_dir,
2826 filters=filters, offset=offset)
2829@require_admin_context
2830@context_manager.reader
2831def transfer_get_all_expired(context):
2832 query = model_query(context, models.Transfer)
2833 expires_at_attr = getattr(models.Transfer, 'expires_at', None)
2834 now_time = timeutils.utcnow()
2835 query = query.filter(expires_at_attr.op('<=')(now_time))
2836 result = query.all()
2838 return result
2841@require_context
2842@handle_db_data_error
2843@context_manager.writer
2844def transfer_create(context, values):
2845 if not values.get('id'): 2845 ↛ 2848line 2845 didn't jump to line 2848 because the condition on line 2845 was always true
2846 values['id'] = uuidutils.generate_uuid()
2848 resource_id = values['resource_id']
2849 now_time = timeutils.utcnow()
2850 time_delta = datetime.timedelta(seconds=CONF.transfer_retention_time)
2851 transfer_timeout = now_time + time_delta
2852 values['expires_at'] = transfer_timeout
2854 transfer = models.Transfer()
2855 transfer.update(values)
2856 transfer.save(session=context.session)
2857 update = {'status': constants.STATUS_AWAITING_TRANSFER}
2858 if values['resource_type'] == 'share': 2858 ↛ 2860line 2858 didn't jump to line 2860 because the condition on line 2858 was always true
2859 _share_update(context, resource_id, update)
2860 return transfer
2863@require_context
2864@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
2865@context_manager.writer
2866def transfer_destroy(context, transfer_id, update_share_status=True):
2867 update = {'status': constants.STATUS_AVAILABLE}
2868 transfer = _transfer_get(context, transfer_id)
2869 if transfer['resource_type'] == 'share': 2869 ↛ 2873line 2869 didn't jump to line 2873 because the condition on line 2869 was always true
2870 if update_share_status: 2870 ↛ 2873line 2870 didn't jump to line 2873 because the condition on line 2870 was always true
2871 _share_update(context, transfer['resource_id'], update)
2873 transfer_query = model_query(
2874 context, models.Transfer,
2875 ).filter_by(
2876 id=transfer_id,
2877 )
2878 transfer_query.soft_delete()
2881@require_context
2882@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
2883@context_manager.writer
2884def transfer_accept(
2885 context, transfer_id, user_id, project_id, accept_snapshots=False,
2886):
2887 share_id = _transfer_get(context, transfer_id)['resource_id']
2888 update = {
2889 'status': constants.STATUS_AVAILABLE,
2890 'user_id': user_id,
2891 'project_id': project_id,
2892 'updated_at': timeutils.utcnow(),
2893 }
2894 _share_update(context, share_id, update)
2896 # Update snapshots for transfer snapshots with share.
2897 if accept_snapshots: 2897 ↛ 2898line 2897 didn't jump to line 2898 because the condition on line 2897 was never true
2898 snapshots = _share_snapshot_get_all_for_share(context, share_id)
2899 for snapshot in snapshots:
2900 LOG.debug('Begin to transfer snapshot: %s', snapshot['id'])
2901 update = {
2902 'user_id': user_id,
2903 'project_id': project_id,
2904 'updated_at': timeutils.utcnow(),
2905 }
2906 _share_snapshot_update(context, snapshot['id'], update)
2907 query = context.session.query(models.Transfer).filter_by(id=transfer_id)
2908 query.update(
2909 {
2910 'deleted': True,
2911 'deleted_at': timeutils.utcnow(),
2912 'updated_at': timeutils.utcnow(),
2913 'destination_project_id': project_id,
2914 'accepted': True,
2915 }
2916 )
2919@require_context
2920@context_manager.writer
2921def transfer_accept_rollback(
2922 context, transfer_id, user_id, project_id, rollback_snap=False,
2923):
2924 share_id = _transfer_get(
2925 context, transfer_id, read_deleted=True,
2926 )['resource_id']
2927 update = {
2928 'status': constants.STATUS_AWAITING_TRANSFER,
2929 'user_id': user_id,
2930 'project_id': project_id,
2931 'updated_at': timeutils.utcnow(),
2932 }
2933 _share_update(context, share_id, update)
2935 # rollback snapshots for transfer snapshots with share.
2936 if rollback_snap: 2936 ↛ 2937line 2936 didn't jump to line 2937 because the condition on line 2936 was never true
2937 snapshots = _share_snapshot_get_all_for_share(context, share_id)
2938 for snapshot in snapshots:
2939 LOG.debug('Begin to rollback snapshot: %s', snapshot['id'])
2940 update = {
2941 'user_id': user_id,
2942 'project_id': project_id,
2943 'updated_at': timeutils.utcnow(),
2944 }
2945 _share_snapshot_update(context, snapshot['id'], update)
2947 query = context.session.query(models.Transfer).filter_by(id=transfer_id)
2948 query.update(
2949 {
2950 'deleted': 'False',
2951 'deleted_at': None,
2952 'updated_at': timeutils.utcnow(),
2953 'destination_project_id': None,
2954 'accepted': 0,
2955 }
2956 )
2959###################
2962def _share_access_get_query(context, values, read_deleted='no'):
2963 """Get access record."""
2964 query = model_query(
2965 context, models.ShareAccessMapping,
2966 read_deleted=read_deleted
2967 ).options(
2968 orm.joinedload(models.ShareAccessMapping.share_access_rules_metadata),
2969 )
2970 return query.filter_by(**values)
2973def _share_instance_access_query(context, access_id=None, instance_id=None):
2974 filters = {'deleted': 'False'}
2976 if access_id is not None:
2977 filters.update({'access_id': access_id})
2979 if instance_id is not None:
2980 filters.update({'share_instance_id': instance_id})
2982 return model_query(
2983 context, models.ShareInstanceAccessMapping,
2984 ).filter_by(**filters)
2987def _share_access_metadata_get_item(context, access_id, key):
2988 result = _share_access_metadata_get_query(
2989 context, access_id,
2990 ).filter_by(key=key).first()
2991 if not result:
2992 raise exception.ShareAccessMetadataNotFound(
2993 metadata_key=key, access_id=access_id)
2994 return result
2997def _share_access_metadata_get_query(context, access_id):
2998 return model_query(
2999 context, models.ShareAccessRulesMetadata, read_deleted="no",
3000 ).filter_by(
3001 access_id=access_id,
3002 ).options(orm.joinedload(models.ShareAccessRulesMetadata.access))
3005@require_context
3006@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
3007@context_manager.writer
3008def share_access_metadata_update(context, access_id, metadata):
3009 # Now update all existing items with new values, or create new meta
3010 # objects
3011 for meta_key, meta_value in metadata.items():
3013 # update the value whether it exists or not
3014 item = {"value": meta_value}
3015 try:
3016 meta_ref = _share_access_metadata_get_item(
3017 context, access_id, meta_key,
3018 )
3019 except exception.ShareAccessMetadataNotFound:
3020 meta_ref = models.ShareAccessRulesMetadata()
3021 item.update({"key": meta_key, "access_id": access_id})
3023 meta_ref.update(item)
3024 meta_ref.save(session=context.session)
3026 return metadata
3029@require_context
3030@context_manager.writer
3031def share_access_metadata_delete(context, access_id, key):
3032 metadata = _share_access_metadata_get_item(
3033 context, access_id, key,
3034 )
3035 metadata.soft_delete(session=context.session)
3038@require_context
3039@context_manager.writer
3040def share_access_create(context, values):
3041 values = ensure_model_dict_has_id(values)
3042 values['share_access_rules_metadata'] = _metadata_refs(
3043 values.get('metadata'), models.ShareAccessRulesMetadata
3044 )
3046 access_ref = models.ShareAccessMapping()
3047 access_ref.update(values)
3048 access_ref.save(session=context.session)
3050 parent_share = _share_get(context, values['share_id'])
3052 for instance in parent_share.instances:
3053 values = {
3054 'share_instance_id': instance['id'],
3055 'access_id': access_ref['id'],
3056 }
3058 instance_access_ref = models.ShareInstanceAccessMapping()
3059 instance_access_ref.update(ensure_model_dict_has_id(values))
3060 instance_access_ref.save(session=context.session)
3062 return _share_access_get(context, access_ref['id'])
3065@require_context
3066@context_manager.writer
3067def share_access_update(context, access_id, values):
3068 access_ref = _share_access_get(context, access_id)
3069 access_ref.update(values)
3070 access_ref.save(session=context.session)
3071 return access_ref
3074@require_context
3075@context_manager.writer
3076def share_instance_access_create(context, values, share_instance_id):
3077 values = ensure_model_dict_has_id(values)
3078 access_list = _share_access_get_query(
3079 context,
3080 {
3081 'share_id': values['share_id'],
3082 'access_type': values['access_type'],
3083 'access_to': values['access_to'],
3084 }
3085 ).all()
3086 if len(access_list) > 0:
3087 access_ref = access_list[0]
3088 else:
3089 access_ref = models.ShareAccessMapping()
3090 access_ref.update(values)
3091 access_ref.save(session=context.session)
3093 values = {
3094 'share_instance_id': share_instance_id,
3095 'access_id': access_ref['id'],
3096 }
3098 instance_access_ref = models.ShareInstanceAccessMapping()
3099 instance_access_ref.update(ensure_model_dict_has_id(values))
3100 instance_access_ref.save(session=context.session)
3102 return _share_access_get(context, access_ref['id'])
3105@require_context
3106@context_manager.writer
3107def share_instance_access_copy(context, share_id, instance_id):
3108 """Copy access rules from share to share instance."""
3109 share_access_rules = _share_access_get_query(
3110 context, {'share_id': share_id}
3111 ).all()
3113 for access_rule in share_access_rules:
3114 values = {
3115 'share_instance_id': instance_id,
3116 'access_id': access_rule['id'],
3117 }
3119 instance_access_ref = models.ShareInstanceAccessMapping()
3120 instance_access_ref.update(ensure_model_dict_has_id(values))
3121 instance_access_ref.save(session=context.session)
3123 return share_access_rules
3126@require_context
3127@context_manager.reader
3128def share_access_get(context, access_id):
3129 return _share_access_get(context, access_id)
3132def _share_access_get(context, access_id):
3133 """Get access record."""
3134 access = _share_access_get_query(context, {'id': access_id}).first()
3135 if access:
3136 return access
3137 else:
3138 raise exception.NotFound()
3141@require_context
3142@context_manager.reader
3143def share_access_get_with_context(context, access_id):
3144 """Get access record."""
3145 access = _share_access_get_query(
3146 context, {'id': access_id}
3147 ).options(orm.joinedload(models.ShareAccessMapping.share)).first()
3148 if access:
3149 access['project_id'] = access['share']['project_id']
3150 return access
3151 else:
3152 raise exception.NotFound()
3155@require_context
3156@context_manager.reader
3157def share_instance_access_get(context, access_id, instance_id,
3158 with_share_access_data=True):
3159 """Get access record."""
3160 access = _share_instance_access_query(
3161 context, access_id, instance_id
3162 ).first()
3163 if access is None:
3164 raise exception.NotFound()
3166 if with_share_access_data:
3167 access = _set_instances_share_access_data(context, access)[0]
3169 return access
3172@require_context
3173@context_manager.reader
3174def share_access_get_all_for_share(context, share_id, filters=None):
3175 filters = filters or {}
3176 share_access_mapping = models.ShareAccessMapping
3177 query = _share_access_get_query(
3178 context, {'share_id': share_id}
3179 ).filter(
3180 models.ShareAccessMapping.instance_mappings.any()
3181 )
3183 legal_filter_keys = ('id', 'access_type', 'access_key',
3184 'access_to', 'access_level')
3186 if 'metadata' in filters:
3187 for k, v in filters['metadata'].items():
3188 query = query.filter(
3189 or_(
3190 models.ShareAccessMapping.
3191 share_access_rules_metadata.any(key=k, value=v)
3192 )
3193 )
3195 query = exact_filter(
3196 query, share_access_mapping, filters, legal_filter_keys)
3198 return query.all()
3201@require_context
3202@context_manager.reader
3203def share_access_get_all_for_instance(context, instance_id, filters=None,
3204 with_share_access_data=True):
3205 """Get all access rules related to a certain share instance."""
3206 filters = copy.deepcopy(filters) if filters else {}
3207 filters.update({'share_instance_id': instance_id})
3208 legal_filter_keys = ('id', 'share_instance_id', 'access_id', 'state')
3209 query = _share_instance_access_query(context)
3211 query = exact_filter(
3212 query, models.ShareInstanceAccessMapping, filters, legal_filter_keys)
3214 instance_accesses = query.all()
3216 if with_share_access_data:
3217 instance_accesses = _set_instances_share_access_data(
3218 context, instance_accesses
3219 )
3221 return instance_accesses
3224def _set_instances_share_access_data(context, instance_accesses):
3225 if instance_accesses and not isinstance(instance_accesses, list):
3226 instance_accesses = [instance_accesses]
3228 for instance_access in instance_accesses:
3229 share_access = _share_access_get(
3230 context, instance_access['access_id']
3231 )
3232 instance_access.set_share_access_data(share_access)
3234 return instance_accesses
3237def _set_instances_snapshot_access_data(context, instance_accesses):
3238 if instance_accesses and not isinstance(instance_accesses, list):
3239 instance_accesses = [instance_accesses]
3241 for instance_access in instance_accesses:
3242 snapshot_access = _share_snapshot_access_get(
3243 context, instance_access['access_id']
3244 )
3245 instance_access.set_snapshot_access_data(snapshot_access)
3247 return instance_accesses
3250@require_context
3251@context_manager.reader
3252def share_access_get_all_by_type_and_access(context, share_id, access_type,
3253 access):
3254 return _share_access_get_query(context,
3255 {'share_id': share_id,
3256 'access_type': access_type,
3257 'access_to': access}).all()
3260@require_context
3261@context_manager.reader
3262def share_access_check_for_existing_access(context, share_id, access_type,
3263 access_to):
3264 return _check_for_existing_access(
3265 context, 'share', share_id, access_type, access_to)
3268def _check_for_existing_access(context, resource, resource_id, access_type,
3269 access_to):
3270 if resource == 'share':
3271 query_method = _share_access_get_query
3272 access_to_field = models.ShareAccessMapping.access_to
3273 else:
3274 query_method = _share_snapshot_access_get_query
3275 access_to_field = models.ShareSnapshotAccessMapping.access_to
3277 if access_type == 'ip':
3278 rules = query_method(
3279 context,
3280 {'%s_id' % resource: resource_id, 'access_type': access_type}
3281 ).filter(access_to_field.startswith(access_to.split('/')[0])).all()
3283 matching_rules = [
3284 rule for rule in rules if
3285 ipaddress.ip_network(str(access_to)) ==
3286 ipaddress.ip_network(str(rule['access_to']))
3287 ]
3288 return len(matching_rules) > 0
3290 return query_method(
3291 context,
3292 {
3293 '%s_id' % resource: resource_id,
3294 'access_type': access_type,
3295 'access_to': access_to
3296 }
3297 ).count() > 0
3300@require_context
3301@context_manager.writer
3302def share_instance_access_delete(context, mapping_id):
3303 mapping = context.session.query(
3304 models.ShareInstanceAccessMapping
3305 ).filter_by(id=mapping_id).first()
3307 if not mapping: 3307 ↛ 3308line 3307 didn't jump to line 3308 because the condition on line 3307 was never true
3308 exception.NotFound()
3310 filters = {
3311 'resource_id': mapping['access_id'],
3312 'all_projects': True
3313 }
3314 locks, _ = resource_lock_get_all(
3315 context.elevated(), filters=filters
3316 )
3317 if locks:
3318 for lock in locks:
3319 if lock['resource_action'] == constants.RESOURCE_ACTION_DELETE: 3319 ↛ 3336line 3319 didn't jump to line 3336 because the condition on line 3319 was always true
3320 lock_reason = (
3321 constants.SHARE_LOCKED_BY_ACCESS_LOCK_REASON % {
3322 'lock_id': lock['id']
3323 }
3324 )
3325 share_filters = {
3326 'all_projects': True,
3327 'lock_reason': lock_reason
3328 }
3329 share_locks, _ = resource_lock_get_all(
3330 context.elevated(), filters=share_filters
3331 ) or []
3332 for share_lock in share_locks:
3333 resource_lock_delete(
3334 context.elevated(), share_lock['id']
3335 )
3336 resource_lock_delete(
3337 context.elevated(), lock['id']
3338 )
3340 mapping.soft_delete(
3341 session=context.session, update_status=True,
3342 status_field_name='state'
3343 )
3345 other_mappings = _share_instance_access_query(
3346 context, mapping['access_id']
3347 ).all()
3349 # NOTE(u_glide): Remove access rule if all mappings were removed.
3350 if len(other_mappings) == 0: 3350 ↛ exitline 3350 didn't return from function 'share_instance_access_delete' because the condition on line 3350 was always true
3351 context.session.query(models.ShareAccessRulesMetadata).filter_by(
3352 access_id=mapping['access_id']
3353 ).soft_delete()
3354 context.session.query(models.ShareAccessMapping).filter_by(
3355 id=mapping['access_id']
3356 ).soft_delete()
3359@require_context
3360@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
3361@context_manager.writer
3362def share_instance_access_update(context, access_id, instance_id, updates):
3363 share_access_fields = ('access_type', 'access_to', 'access_key',
3364 'access_level')
3366 share_access_map_updates, share_instance_access_map_updates = (
3367 _extract_subdict_by_fields(updates, share_access_fields)
3368 )
3369 updated_at = timeutils.utcnow()
3370 share_access_map_updates['updated_at'] = updated_at
3371 share_instance_access_map_updates['updated_at'] = updated_at
3373 access_ref = _share_access_get_query(context, {'id': access_id}).first()
3374 access_ref.update(share_access_map_updates)
3375 access_ref.save(session=context.session)
3377 instance_access_ref = _share_instance_access_query(
3378 context, access_id, instance_id).first()
3379 instance_access_ref.update(share_instance_access_map_updates)
3380 instance_access_ref.save(session=context.session)
3382 return instance_access_ref
3384###################
3387@require_context
3388@context_manager.writer
3389def share_snapshot_instance_create(context, snapshot_id, values):
3390 return _share_snapshot_instance_create(context, snapshot_id, values)
3393def _share_snapshot_instance_create(context, snapshot_id, values):
3394 values = copy.deepcopy(values)
3395 values['share_snapshot_metadata'] = _metadata_refs(
3396 values.get('metadata'), models.ShareSnapshotMetadata)
3398 _change_size_to_instance_size(values)
3400 if not values.get('id'):
3401 values['id'] = uuidutils.generate_uuid()
3402 values.update({'snapshot_id': snapshot_id})
3404 instance_ref = models.ShareSnapshotInstance()
3405 instance_ref.update(values)
3406 instance_ref.save(session=context.session)
3408 return _share_snapshot_instance_get(context, instance_ref['id'])
3411@require_context
3412@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
3413@context_manager.writer
3414def share_snapshot_instance_update(context, instance_id, values):
3415 instance_ref = _share_snapshot_instance_get(context, instance_id)
3416 _change_size_to_instance_size(values)
3418 # NOTE(u_glide): Ignore updates to custom properties
3419 for extra_key in models.ShareSnapshotInstance._extra_keys:
3420 if extra_key in values: 3420 ↛ 3421line 3420 didn't jump to line 3421 because the condition on line 3420 was never true
3421 values.pop(extra_key)
3423 instance_ref.update(values)
3424 instance_ref.save(session=context.session)
3425 return instance_ref
3428@require_context
3429@context_manager.writer
3430def share_snapshot_instance_delete(context, snapshot_instance_id):
3431 snapshot_instance_ref = _share_snapshot_instance_get(
3432 context, snapshot_instance_id
3433 )
3435 access_rules = _share_snapshot_access_get_all_for_snapshot_instance(
3436 context, snapshot_instance_id
3437 )
3438 for rule in access_rules: 3438 ↛ 3439line 3438 didn't jump to line 3439 because the loop on line 3438 never started
3439 _share_snapshot_instance_access_delete(
3440 context, rule['access_id'], snapshot_instance_id,
3441 )
3443 for el in snapshot_instance_ref.export_locations: 3443 ↛ 3444line 3443 didn't jump to line 3444 because the loop on line 3443 never started
3444 _share_snapshot_instance_export_location_delete(context, el['id'])
3446 snapshot_instance_ref.soft_delete(
3447 session=context.session, update_status=True)
3448 snapshot = _share_snapshot_get(
3449 context, snapshot_instance_ref['snapshot_id'])
3450 if len(snapshot.instances) == 0:
3451 context.session.query(models.ShareSnapshotMetadata).filter_by(
3452 share_snapshot_id=snapshot['id'],
3453 ).soft_delete()
3454 snapshot.soft_delete(session=context.session)
3457@require_context
3458@context_manager.reader
3459def share_snapshot_instance_get(context, snapshot_instance_id,
3460 with_share_data=False):
3461 return _share_snapshot_instance_get(
3462 context, snapshot_instance_id, with_share_data=with_share_data,
3463 )
3466def _share_snapshot_instance_get(
3467 context, snapshot_instance_id, with_share_data=False,
3468):
3469 result = _share_snapshot_instance_get_with_filters(
3470 context, instance_ids=[snapshot_instance_id],
3471 ).first()
3473 if result is None: 3473 ↛ 3474line 3473 didn't jump to line 3474 because the condition on line 3473 was never true
3474 raise exception.ShareSnapshotInstanceNotFound(
3475 instance_id=snapshot_instance_id)
3477 if with_share_data:
3478 result = _set_share_snapshot_instance_data(context, result)[0]
3480 return result
3483@require_context
3484@context_manager.reader
3485def share_snapshot_instance_get_all_with_filters(
3486 context, search_filters, with_share_data=False,
3487):
3488 """Get snapshot instances filtered by known attrs, ignore unknown attrs.
3490 All filters accept list/tuples to filter on, along with simple values.
3491 """
3492 return _share_snapshot_instance_get_all_with_filters(
3493 context, search_filters, with_share_data=with_share_data,
3494 )
3497def _share_snapshot_instance_get_all_with_filters(
3498 context, search_filters, with_share_data=False,
3499):
3500 def listify(values):
3501 if values:
3502 if not isinstance(values, (list, tuple, set)):
3503 return values,
3504 else:
3505 return values
3507 _known_filters = ('instance_ids', 'snapshot_ids', 'share_instance_ids',
3508 'statuses')
3510 filters = {k: listify(search_filters.get(k)) for k in _known_filters}
3512 result = _share_snapshot_instance_get_with_filters(
3513 context, **filters,
3514 ).all()
3516 if with_share_data:
3517 result = _set_share_snapshot_instance_data(context, result)
3519 return result
3522def _share_snapshot_instance_get_with_filters(context, instance_ids=None,
3523 snapshot_ids=None, statuses=None,
3524 share_instance_ids=None):
3525 query = model_query(context, models.ShareSnapshotInstance,
3526 read_deleted="no")
3528 if instance_ids is not None:
3529 query = query.filter(
3530 models.ShareSnapshotInstance.id.in_(instance_ids))
3532 if snapshot_ids is not None:
3533 query = query.filter(
3534 models.ShareSnapshotInstance.snapshot_id.in_(snapshot_ids))
3536 if share_instance_ids is not None:
3537 query = query.filter(models.ShareSnapshotInstance.share_instance_id
3538 .in_(share_instance_ids))
3540 if statuses is not None:
3541 query = query.filter(models.ShareSnapshotInstance.status.in_(statuses))
3543 query = query.options(
3544 orm.joinedload(models.ShareSnapshotInstance.share_group_snapshot),
3545 )
3546 return query
3549def _set_share_snapshot_instance_data(context, snapshot_instances):
3550 if snapshot_instances and not isinstance(snapshot_instances, list):
3551 snapshot_instances = [snapshot_instances]
3553 for snapshot_instance in snapshot_instances:
3554 share_instance = _share_instance_get(
3555 context, snapshot_instance['share_instance_id'],
3556 with_share_data=True)
3557 snapshot_instance['share'] = share_instance
3559 return snapshot_instances
3562###################
3565@require_context
3566@context_manager.writer
3567def share_snapshot_create(context, create_values,
3568 create_snapshot_instance=True):
3569 values = copy.deepcopy(create_values)
3570 values = ensure_model_dict_has_id(values)
3571 values['share_snapshot_metadata'] = _metadata_refs(
3572 values.pop('metadata', {}), models.ShareSnapshotMetadata)
3574 snapshot_ref = models.ShareSnapshot()
3575 snapshot_instance_values, snapshot_values = (
3576 _extract_snapshot_instance_values(values)
3577 )
3578 snapshot_ref.update(snapshot_values)
3580 share_ref = _share_get(
3581 context,
3582 snapshot_values.get('share_id'),
3583 )
3584 snapshot_instance_values.update(
3585 {'share_instance_id': share_ref.instance.id}
3586 )
3588 snapshot_ref.save(session=context.session)
3590 if create_snapshot_instance:
3591 _share_snapshot_instance_create(
3592 context,
3593 snapshot_ref['id'],
3594 snapshot_instance_values,
3595 )
3596 return _share_snapshot_get(context, snapshot_values['id'])
3599@require_admin_context
3600def _snapshot_data_get_for_project(
3601 context, project_id, user_id, share_type_id=None,
3602):
3603 query = model_query(
3604 context, models.ShareSnapshot,
3605 func.count(models.ShareSnapshot.id),
3606 func.sum(models.ShareSnapshot.size),
3607 read_deleted="no",
3608 ).filter_by(project_id=project_id)
3609 if share_type_id:
3610 query = query.join(
3611 models.ShareInstance,
3612 models.ShareInstance.share_id == models.ShareSnapshot.share_id,
3613 ).filter_by(share_type_id=share_type_id)
3614 elif user_id:
3615 query = query.filter_by(user_id=user_id)
3616 result = query.first()
3618 return result[0] or 0, result[1] or 0
3621@require_context
3622@context_manager.reader
3623def share_snapshot_get(context, snapshot_id, project_only=True):
3624 return _share_snapshot_get(context, snapshot_id, project_only=project_only)
3627def _share_snapshot_get(context, snapshot_id, project_only=True):
3628 result = model_query(
3629 context, models.ShareSnapshot, project_only=project_only,
3630 ).filter_by(
3631 id=snapshot_id,
3632 ).options(
3633 orm.joinedload(models.ShareSnapshot.share),
3634 orm.joinedload(models.ShareSnapshot.instances),
3635 orm.joinedload(models.ShareSnapshot.share_snapshot_metadata),
3636 ).first()
3638 if not result:
3639 raise exception.ShareSnapshotNotFound(snapshot_id=snapshot_id)
3641 return result
3644def _share_snapshot_get_all_with_filters(context, project_id=None,
3645 share_id=None, filters=None,
3646 limit=None, offset=None,
3647 sort_key=None, sort_dir=None,
3648 show_count=False):
3649 """Retrieves all snapshots.
3651 If no sorting parameters are specified then returned snapshots are sorted
3652 by the 'created_at' key and desc order.
3654 :param context: context to query under
3655 :param filters: dictionary of filters
3656 :param limit: maximum number of items to return
3657 :param sort_key: attribute by which results should be sorted,default is
3658 created_at
3659 :param sort_dir: direction in which results should be sorted
3660 :returns: list of matching snapshots
3661 """
3662 # Init data
3663 sort_key = sort_key or 'created_at'
3664 sort_dir = sort_dir or 'desc'
3665 filters = copy.deepcopy(filters) if filters else {}
3666 query = model_query(context, models.ShareSnapshot)
3668 if project_id: 3668 ↛ 3669line 3668 didn't jump to line 3669 because the condition on line 3668 was never true
3669 query = query.filter_by(project_id=project_id)
3671 if share_id:
3672 query = query.filter_by(share_id=share_id)
3674 query = query.options(
3675 orm.joinedload(models.ShareSnapshot.share),
3676 orm.joinedload(models.ShareSnapshot.instances),
3677 orm.joinedload(models.ShareSnapshot.share_snapshot_metadata),
3678 )
3680 # Snapshots with no instances are filtered out.
3681 query = query.filter(
3682 models.ShareSnapshot.id == models.ShareSnapshotInstance.snapshot_id)
3684 # Apply filters
3685 if 'usage' in filters: 3685 ↛ 3686line 3685 didn't jump to line 3686 because the condition on line 3685 was never true
3686 usage_filter_keys = ['any', 'used', 'unused']
3687 if filters['usage'] == 'any':
3688 pass
3689 elif filters['usage'] == 'used':
3690 query = query.filter(models.Share.snapshot_id == (
3691 models.ShareSnapshot.id))
3692 elif filters['usage'] == 'unused':
3693 query = query.filter(models.Share.snapshot_id != (
3694 models.ShareSnapshot.id))
3695 else:
3696 msg = _("Wrong 'usage' key provided - '%(key)s'. "
3697 "Expected keys are '%(ek)s'.") % {
3698 'key': filters['usage'],
3699 'ek': usage_filter_keys}
3700 raise exception.InvalidInput(reason=msg)
3701 filters.pop('usage')
3703 if 'status' in filters:
3704 query = query.filter(models.ShareSnapshotInstance.status == (
3705 filters['status']))
3706 filters.pop('status')
3708 if 'metadata' in filters:
3709 for k, v in filters['metadata'].items():
3710 # pylint: disable=no-member
3711 query = query.filter(
3712 or_(models.ShareSnapshot.share_snapshot_metadata.any(
3713 key=k, value=v)))
3714 filters.pop('metadata')
3716 legal_filter_keys = ('display_name', 'display_name~',
3717 'display_description', 'display_description~',
3718 'id', 'user_id', 'project_id', 'share_id',
3719 'share_proto', 'size', 'share_size')
3720 query = exact_filter(query, models.ShareSnapshot,
3721 filters, legal_filter_keys)
3723 if not filters.get('list_deferred_delete'): 3723 ↛ 3730line 3723 didn't jump to line 3730 because the condition on line 3723 was always true
3724 query = query.filter(and_(
3725 models.ShareSnapshotInstance.status != (
3726 constants.STATUS_DEFERRED_DELETING),
3727 models.ShareSnapshotInstance.status != (
3728 constants.STATUS_ERROR_DEFERRED_DELETING)))
3730 query = apply_sorting(models.ShareSnapshot, query, sort_key, sort_dir)
3732 count = None
3733 if show_count:
3734 count = query.order_by(models.ShareSnapshot.id).distinct().count()
3736 if limit is not None:
3737 query = query.limit(limit)
3739 if offset: 3739 ↛ 3740line 3739 didn't jump to line 3740 because the condition on line 3739 was never true
3740 query = query.offset(offset)
3742 # Returns list of share snapshots that satisfy filters
3743 query = query.all()
3745 if show_count:
3746 return count, query
3748 return query
3751@require_admin_context
3752@context_manager.reader
3753def share_snapshot_get_all(context, filters=None, limit=None, offset=None,
3754 sort_key=None, sort_dir=None):
3755 return _share_snapshot_get_all_with_filters(
3756 context, filters=filters, limit=limit,
3757 offset=offset, sort_key=sort_key, sort_dir=sort_dir)
3760@require_admin_context
3761@context_manager.reader
3762def share_snapshot_get_all_with_count(context, filters=None, limit=None,
3763 offset=None, sort_key=None,
3764 sort_dir=None):
3765 count, query = _share_snapshot_get_all_with_filters(
3766 context, filters=filters, limit=limit,
3767 offset=offset, sort_key=sort_key, sort_dir=sort_dir,
3768 show_count=True)
3769 return count, query
3772@require_context
3773@context_manager.reader
3774def share_snapshot_get_all_by_project(context, project_id, filters=None,
3775 limit=None, offset=None,
3776 sort_key=None, sort_dir=None):
3777 authorize_project_context(context, project_id)
3778 return _share_snapshot_get_all_with_filters(
3779 context, project_id=project_id, filters=filters, limit=limit,
3780 offset=offset, sort_key=sort_key, sort_dir=sort_dir)
3783@require_context
3784@context_manager.reader
3785def share_snapshot_get_all_by_project_with_count(context, project_id,
3786 filters=None, limit=None,
3787 offset=None, sort_key=None,
3788 sort_dir=None):
3789 authorize_project_context(context, project_id)
3790 count, query = _share_snapshot_get_all_with_filters(
3791 context, project_id=project_id, filters=filters, limit=limit,
3792 offset=offset, sort_key=sort_key, sort_dir=sort_dir,
3793 show_count=True)
3794 return count, query
3797@require_context
3798@context_manager.reader
3799def share_snapshot_get_all_for_share(
3800 context, share_id, filters=None, sort_key=None, sort_dir=None,
3801):
3802 return _share_snapshot_get_all_for_share(
3803 context, share_id, filters=None, sort_key=None, sort_dir=None,
3804 )
3807def _share_snapshot_get_all_for_share(
3808 context, share_id, filters=None, sort_key=None, sort_dir=None,
3809):
3810 return _share_snapshot_get_all_with_filters(
3811 context, share_id=share_id,
3812 filters=filters, sort_key=sort_key, sort_dir=sort_dir,
3813 )
3816@require_context
3817@context_manager.reader
3818def share_snapshot_get_latest_for_share(context, share_id):
3819 snapshots = _share_snapshot_get_all_with_filters(
3820 context, share_id=share_id, sort_key='created_at', sort_dir='desc')
3821 return snapshots[0] if snapshots else None
3824@require_context
3825@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
3826@context_manager.writer
3827def share_snapshot_update(context, snapshot_id, values):
3828 return _share_snapshot_update(context, snapshot_id, values)
3831def _share_snapshot_update(context, snapshot_id, values):
3832 snapshot_ref = _share_snapshot_get(context, snapshot_id)
3834 instance_values, snapshot_values = (
3835 _extract_snapshot_instance_values(values)
3836 )
3838 if snapshot_values:
3839 snapshot_ref.update(snapshot_values)
3840 snapshot_ref.save(session=context.session)
3842 if instance_values: 3842 ↛ 3846line 3842 didn't jump to line 3846 because the condition on line 3842 was always true
3843 snapshot_ref.instance.update(instance_values)
3844 snapshot_ref.instance.save(session=context.session)
3846 return snapshot_ref
3849@require_context
3850@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
3851@context_manager.writer
3852def share_snapshot_instances_status_update(
3853 context, snapshot_instance_ids, values,
3854):
3855 return _share_snapshot_instances_status_update(
3856 context, snapshot_instance_ids, values,
3857 )
3860def _share_snapshot_instances_status_update(
3861 context, snapshot_instance_ids, values,
3862):
3863 result = model_query(
3864 context, models.ShareSnapshotInstance,
3865 read_deleted="no",
3866 ).filter(
3867 models.ShareSnapshotInstance.id.in_(snapshot_instance_ids)
3868 ).update(values, synchronize_session=False)
3870 return result
3873###################################
3874# Share Snapshot Metadata functions
3875###################################
3877@require_context
3878@require_share_snapshot_exists
3879@context_manager.writer
3880def share_snapshot_metadata_get(context, share_snapshot_id):
3881 return _share_snapshot_metadata_get(context, share_snapshot_id)
3884@require_context
3885@require_share_snapshot_exists
3886@context_manager.writer
3887def share_snapshot_metadata_delete(context, share_snapshot_id, key):
3888 meta_ref = _share_snapshot_metadata_get_item(
3889 context, share_snapshot_id, key)
3890 meta_ref.soft_delete(session=context.session)
3893@require_context
3894@require_share_snapshot_exists
3895@context_manager.writer
3896def share_snapshot_metadata_update(context, share_snapshot_id,
3897 metadata, delete):
3898 return _share_snapshot_metadata_update(context, share_snapshot_id,
3899 metadata, delete)
3902@context_manager.writer
3903def share_snapshot_metadata_update_item(context, share_snapshot_id, item):
3904 return _share_snapshot_metadata_update(context, share_snapshot_id,
3905 item, delete=False)
3908@context_manager.reader
3909def share_snapshot_metadata_get_item(context, share_snapshot_id, key):
3911 row = _share_snapshot_metadata_get_item(context, share_snapshot_id, key)
3912 result = {}
3913 result[row['key']] = row['value']
3915 return result
3918def _share_snapshot_metadata_get_query(context, share_snapshot_id):
3919 return model_query(
3920 context, models.ShareSnapshotMetadata, read_deleted="no",
3921 ).filter_by(
3922 share_snapshot_id=share_snapshot_id,
3923 ).options(orm.joinedload(models.ShareSnapshotMetadata.share_snapshot))
3926def _share_snapshot_metadata_get(context, share_snapshot_id):
3927 rows = _share_snapshot_metadata_get_query(
3928 context, share_snapshot_id,
3929 ).all()
3931 result = {}
3932 for row in rows:
3933 result[row['key']] = row['value']
3934 return result
3937def _share_snapshot_metadata_get_item(context, share_snapshot_id, key):
3938 result = _share_snapshot_metadata_get_query(
3939 context, share_snapshot_id).filter_by(
3940 key=key).first()
3941 if not result: 3941 ↛ 3942line 3941 didn't jump to line 3942 because the condition on line 3941 was never true
3942 raise exception.MetadataItemNotFound
3943 return result
3946@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
3947def _share_snapshot_metadata_update(context, share_snapshot_id,
3948 metadata, delete):
3949 delete = strutils.bool_from_string(delete)
3950 if delete: 3950 ↛ 3951line 3950 didn't jump to line 3951 because the condition on line 3950 was never true
3951 original_metadata = _share_snapshot_metadata_get(
3952 context, share_snapshot_id)
3953 for meta_key, meta_value in original_metadata.items():
3954 if meta_key not in metadata:
3955 meta_ref = _share_snapshot_metadata_get_item(
3956 context, share_snapshot_id, meta_key,
3957 )
3958 meta_ref.soft_delete(session=context.session)
3960 # Now update all existing items with new values, or create new meta
3961 # objects
3962 meta_ref = None
3963 for meta_key, meta_value in metadata.items():
3964 # update the value whether it exists or not
3965 item = {"value": meta_value}
3966 meta_ref = _share_snapshot_metadata_get_query(
3967 context, share_snapshot_id,
3968 ).filter_by(key=meta_key).first()
3969 if not meta_ref:
3970 meta_ref = models.ShareSnapshotMetadata()
3971 item.update({"key": meta_key,
3972 "share_snapshot_id": share_snapshot_id})
3973 meta_ref.update(item)
3974 meta_ref.save(session=context.session)
3976 return metadata
3978#################################
3981@require_context
3982@context_manager.writer
3983def share_snapshot_access_create(context, values):
3984 values = ensure_model_dict_has_id(values)
3985 access_ref = models.ShareSnapshotAccessMapping()
3986 access_ref.update(values)
3987 access_ref.save(session=context.session)
3989 snapshot = _share_snapshot_get(context, values['share_snapshot_id'])
3991 for instance in snapshot.instances:
3992 values = {
3993 'share_snapshot_instance_id': instance['id'],
3994 'access_id': access_ref['id'],
3995 }
3997 instance_access_ref = models.ShareSnapshotInstanceAccessMapping()
3998 instance_access_ref.update(ensure_model_dict_has_id(values))
3999 instance_access_ref.save(session=context.session)
4001 return _share_snapshot_access_get(context, access_ref['id'])
4004def _share_snapshot_access_get_query(context, filters, read_deleted='no'):
4005 query = model_query(
4006 context, models.ShareSnapshotAccessMapping, read_deleted=read_deleted
4007 )
4008 return query.filter_by(**filters)
4011def _share_snapshot_instance_access_get_query(
4012 context, access_id=None, share_snapshot_instance_id=None,
4013):
4014 filters = {'deleted': 'False'}
4016 if access_id is not None:
4017 filters.update({'access_id': access_id})
4019 if share_snapshot_instance_id is not None:
4020 filters.update(
4021 {'share_snapshot_instance_id': share_snapshot_instance_id}
4022 )
4024 return model_query(
4025 context, models.ShareSnapshotInstanceAccessMapping
4026 ).filter_by(**filters)
4029@require_context
4030@context_manager.reader
4031def share_snapshot_instance_access_get_all(context, access_id):
4032 return _share_snapshot_instance_access_get_all(context, access_id)
4035def _share_snapshot_instance_access_get_all(context, access_id):
4036 rules = _share_snapshot_instance_access_get_query(
4037 context, access_id=access_id).all()
4038 return rules
4041@require_context
4042@context_manager.reader
4043def share_snapshot_access_get(context, access_id):
4044 return _share_snapshot_access_get(context, access_id)
4047def _share_snapshot_access_get(context, access_id):
4048 access = _share_snapshot_access_get_query(
4049 context, {'id': access_id},
4050 ).first()
4052 if access: 4052 ↛ 4055line 4052 didn't jump to line 4055 because the condition on line 4052 was always true
4053 return access
4054 else:
4055 raise exception.NotFound()
4058@require_context
4059@context_manager.reader
4060def share_snapshot_access_get_all_for_share_snapshot(context,
4061 share_snapshot_id,
4062 filters):
4063 filters['share_snapshot_id'] = share_snapshot_id
4064 access_list = _share_snapshot_access_get_query(
4065 context, filters
4066 ).all()
4068 return access_list
4071@require_context
4072@context_manager.reader
4073def share_snapshot_check_for_existing_access(context, share_snapshot_id,
4074 access_type, access_to):
4075 return _check_for_existing_access(
4076 context, 'share_snapshot', share_snapshot_id, access_type, access_to)
4079@require_context
4080@context_manager.reader
4081def share_snapshot_access_get_all_for_snapshot_instance(
4082 context, snapshot_instance_id, filters=None,
4083 with_snapshot_access_data=True,
4084):
4085 return _share_snapshot_access_get_all_for_snapshot_instance(
4086 context, snapshot_instance_id, filters=filters,
4087 with_snapshot_access_data=with_snapshot_access_data,
4088 )
4091def _share_snapshot_access_get_all_for_snapshot_instance(
4092 context, snapshot_instance_id, filters=None,
4093 with_snapshot_access_data=True,
4094):
4095 """Get all access rules related to a certain snapshot instance."""
4096 filters = copy.deepcopy(filters) if filters else {}
4097 filters.update({'share_snapshot_instance_id': snapshot_instance_id})
4099 query = _share_snapshot_instance_access_get_query(context)
4101 legal_filter_keys = (
4102 'id', 'share_snapshot_instance_id', 'access_id', 'state')
4104 query = exact_filter(
4105 query, models.ShareSnapshotInstanceAccessMapping, filters,
4106 legal_filter_keys)
4108 instance_accesses = query.all()
4110 if with_snapshot_access_data: 4110 ↛ 4115line 4110 didn't jump to line 4115 because the condition on line 4110 was always true
4111 instance_accesses = _set_instances_snapshot_access_data(
4112 context, instance_accesses
4113 )
4115 return instance_accesses
4118@require_context
4119@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
4120@context_manager.writer
4121def share_snapshot_instance_access_update(
4122 context, access_id, instance_id, updates
4123):
4124 snapshot_access_fields = ('access_type', 'access_to')
4125 snapshot_access_map_updates, share_instance_access_map_updates = (
4126 _extract_subdict_by_fields(updates, snapshot_access_fields)
4127 )
4129 updated_at = timeutils.utcnow()
4130 snapshot_access_map_updates['updated_at'] = updated_at
4131 share_instance_access_map_updates['updated_at'] = updated_at
4133 snapshot_access = _share_snapshot_access_get_query(
4134 context, {'id': access_id}).first()
4135 if not snapshot_access: 4135 ↛ 4136line 4135 didn't jump to line 4136 because the condition on line 4135 was never true
4136 raise exception.NotFound()
4137 snapshot_access.update(snapshot_access_map_updates)
4138 snapshot_access.save(session=context.session)
4140 access = _share_snapshot_instance_access_get_query(
4141 context, access_id=access_id,
4142 share_snapshot_instance_id=instance_id).first()
4143 if not access: 4143 ↛ 4144line 4143 didn't jump to line 4144 because the condition on line 4143 was never true
4144 raise exception.NotFound()
4145 access.update(share_instance_access_map_updates)
4146 access.save(session=context.session)
4148 return access
4151@require_context
4152@context_manager.writer
4153def share_snapshot_instance_access_get(
4154 context, access_id, share_snapshot_instance_id,
4155 with_snapshot_access_data=True
4156):
4157 access = _share_snapshot_instance_access_get_query(
4158 context, access_id=access_id,
4159 share_snapshot_instance_id=share_snapshot_instance_id
4160 ).first()
4162 if access is None: 4162 ↛ 4163line 4162 didn't jump to line 4163 because the condition on line 4162 was never true
4163 raise exception.NotFound()
4165 if with_snapshot_access_data: 4165 ↛ 4168line 4165 didn't jump to line 4168 because the condition on line 4165 was always true
4166 return _set_instances_snapshot_access_data(context, access)[0]
4167 else:
4168 return access
4171@require_context
4172@context_manager.writer
4173def share_snapshot_instance_access_delete(
4174 context, access_id, snapshot_instance_id
4175):
4176 return _share_snapshot_instance_access_delete(
4177 context, access_id, snapshot_instance_id
4178 )
4181def _share_snapshot_instance_access_delete(
4182 context, access_id, snapshot_instance_id
4183):
4184 rule = _share_snapshot_instance_access_get_query(
4185 context, access_id=access_id,
4186 share_snapshot_instance_id=snapshot_instance_id).first()
4188 if not rule: 4188 ↛ 4189line 4188 didn't jump to line 4189 because the condition on line 4188 was never true
4189 exception.NotFound()
4191 rule.soft_delete(
4192 session=context.session, update_status=True,
4193 status_field_name='state')
4195 other_mappings = _share_snapshot_instance_access_get_all(
4196 context, rule['access_id'])
4198 if len(other_mappings) == 0: 4198 ↛ 4199line 4198 didn't jump to line 4199 because the condition on line 4198 was never true
4199 context.session.query(
4200 models.ShareSnapshotAccessMapping
4201 ).filter_by(
4202 id=rule['access_id']
4203 ).soft_delete(
4204 update_status=True, status_field_name='state'
4205 )
4208@require_context
4209@context_manager.writer
4210def share_snapshot_instance_export_location_create(context, values):
4211 values = ensure_model_dict_has_id(values)
4212 ssiel = models.ShareSnapshotInstanceExportLocation()
4213 ssiel.update(values)
4214 ssiel.save(session=context.session)
4216 return ssiel
4219def _share_snapshot_instance_export_locations_get_query(context, values):
4220 query = model_query(context, models.ShareSnapshotInstanceExportLocation)
4221 return query.filter_by(**values)
4224@require_context
4225@context_manager.reader
4226def share_snapshot_export_locations_get(context, snapshot_id):
4227 snapshot = _share_snapshot_get(context, snapshot_id)
4228 ins_ids = [ins['id'] for ins in snapshot.instances]
4229 export_locations = _share_snapshot_instance_export_locations_get_query(
4230 context, {}).filter(
4231 models.ShareSnapshotInstanceExportLocation.
4232 share_snapshot_instance_id.in_(ins_ids)).all()
4233 return export_locations
4236@require_context
4237@context_manager.reader
4238def share_snapshot_instance_export_locations_get_all(
4239 context, share_snapshot_instance_id,
4240):
4241 return _share_snapshot_instance_export_locations_get_all(
4242 context, share_snapshot_instance_id,
4243 )
4246def _share_snapshot_instance_export_locations_get_all(
4247 context, share_snapshot_instance_id,
4248):
4249 export_locations = _share_snapshot_instance_export_locations_get_query(
4250 context,
4251 {'share_snapshot_instance_id': share_snapshot_instance_id},
4252 ).all()
4253 return export_locations
4256@require_context
4257@context_manager.reader
4258def share_snapshot_instance_export_location_get(context, el_id):
4259 export_location = _share_snapshot_instance_export_locations_get_query(
4260 context, {'id': el_id},
4261 ).first()
4263 if export_location: 4263 ↛ 4266line 4263 didn't jump to line 4266 because the condition on line 4263 was always true
4264 return export_location
4265 else:
4266 raise exception.NotFound()
4269@require_context
4270@context_manager.writer
4271def share_snapshot_instance_export_location_delete(context, el_id):
4272 return _share_snapshot_instance_export_location_delete(context, el_id)
4275def _share_snapshot_instance_export_location_delete(context, el_id):
4276 el = _share_snapshot_instance_export_locations_get_query(
4277 context, {'id': el_id}).first()
4279 if not el:
4280 exception.NotFound()
4282 el.soft_delete(session=context.session)
4285@require_context
4286@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
4287@context_manager.writer
4288def share_snapshot_instance_export_locations_update(
4289 context, share_snapshot_instance_id, export_locations, delete,
4290):
4291 # NOTE(dviroel): Lets keep this backward compatibility for driver that
4292 # may still return export_locations as string
4293 if not isinstance(export_locations, (list, tuple, set)): 4293 ↛ 4294line 4293 didn't jump to line 4294 because the condition on line 4293 was never true
4294 export_locations = (export_locations, )
4296 export_locations_as_dicts = []
4297 for el in export_locations:
4298 export_location = el
4299 if isinstance(el, str):
4300 export_location = {
4301 "path": el,
4302 "is_admin_only": False,
4303 }
4304 elif not isinstance(export_location, dict): 4304 ↛ 4307line 4304 didn't jump to line 4307 because the condition on line 4304 was always true
4305 raise exception.ManilaException(
4306 _("Wrong export location type '%s'.") % type(export_location))
4307 export_locations_as_dicts.append(export_location)
4309 export_locations = export_locations_as_dicts
4310 export_locations_paths = [el['path'] for el in export_locations]
4311 current_el_rows = _share_snapshot_instance_export_locations_get_all(
4312 context, share_snapshot_instance_id,
4313 )
4315 def get_path_list_from_rows(rows):
4316 return set([row['path'] for row in rows])
4318 current_el_paths = get_path_list_from_rows(current_el_rows)
4320 def create_indexed_time_dict(key_list):
4321 base = timeutils.utcnow()
4322 return {
4323 # NOTE(u_glide): Incrementing timestamp by microseconds to make
4324 # timestamp order match index order.
4325 key: base + datetime.timedelta(microseconds=index)
4326 for index, key in enumerate(key_list)
4327 }
4329 indexed_update_time = create_indexed_time_dict(export_locations_paths)
4331 for el in current_el_rows:
4332 if delete and el['path'] not in export_locations_paths:
4333 el.soft_delete(session=context.session)
4334 else:
4335 updated_at = indexed_update_time[el['path']]
4336 el.update({
4337 'updated_at': updated_at,
4338 })
4339 el.save(session=context.session)
4341 # Now add new export locations
4342 for el in export_locations:
4343 if el['path'] in current_el_paths:
4344 # Already updated
4345 continue
4347 location_ref = models.ShareSnapshotInstanceExportLocation()
4348 location_ref.update({
4349 'id': uuidutils.generate_uuid(),
4350 'path': el['path'],
4351 'share_snapshot_instance_id': share_snapshot_instance_id,
4352 'updated_at': indexed_update_time[el['path']],
4353 'is_admin_only': el.get('is_admin_only', False),
4354 })
4355 location_ref.save(session=context.session)
4357 return get_path_list_from_rows(
4358 _share_snapshot_instance_export_locations_get_all(
4359 context, share_snapshot_instance_id,
4360 )
4361 )
4363#################################
4366def _share_metadata_get_query(context, share_id):
4367 return model_query(
4368 context, models.ShareMetadata, read_deleted="no",
4369 ).filter_by(
4370 share_id=share_id,
4371 ).options(orm.joinedload(models.ShareMetadata.share))
4374@require_context
4375@require_share_exists
4376@context_manager.reader
4377def share_metadata_get(context, share_id):
4378 return _share_metadata_get(context, share_id)
4381def _share_metadata_get(context, share_id):
4382 rows = _share_metadata_get_query(context, share_id).all()
4383 result = {}
4384 for row in rows:
4385 result[row['key']] = row['value']
4387 return result
4390@require_context
4391@require_share_exists
4392@context_manager.reader
4393def share_metadata_get_item(context, share_id, key):
4394 try:
4395 row = _share_metadata_get_item(context, share_id, key)
4396 except exception.MetadataItemNotFound:
4397 raise exception.MetadataItemNotFound()
4399 result = {}
4400 result[row['key']] = row['value']
4402 return result
4405@require_context
4406@require_share_exists
4407@context_manager.writer
4408def share_metadata_delete(context, share_id, key):
4409 _share_metadata_get_query(
4410 context, share_id,
4411 ).filter_by(key=key).soft_delete()
4414@require_context
4415@require_share_exists
4416@context_manager.writer
4417def share_metadata_update(context, share_id, metadata, delete):
4418 return _share_metadata_update(context, share_id, metadata, delete)
4421@require_context
4422@require_share_exists
4423@context_manager.writer
4424def share_metadata_update_item(context, share_id, item):
4425 return _share_metadata_update(context, share_id, item, delete=False)
4428@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
4429def _share_metadata_update(context, share_id, metadata, delete):
4430 # Set existing metadata to deleted if delete argument is True
4431 delete = strutils.bool_from_string(delete)
4432 if delete:
4433 original_metadata = _share_metadata_get(context, share_id)
4434 for meta_key, meta_value in original_metadata.items():
4435 if meta_key not in metadata:
4436 meta_ref = _share_metadata_get_item(
4437 context, share_id, meta_key,
4438 )
4439 meta_ref.soft_delete(session=context.session)
4441 meta_ref = None
4443 # Now update all existing items with new values, or create new meta
4444 # objects
4445 for meta_key, meta_value in metadata.items():
4447 # update the value whether it exists or not
4448 item = {"value": meta_value}
4450 try:
4451 meta_ref = _share_metadata_get_item(
4452 context, share_id, meta_key,
4453 )
4454 except exception.MetadataItemNotFound:
4455 meta_ref = models.ShareMetadata()
4456 item.update({"key": meta_key, "share_id": share_id})
4458 meta_ref.update(item)
4459 meta_ref.save(session=context.session)
4461 return metadata
4464def _share_metadata_get_item(context, share_id, key):
4465 result = _share_metadata_get_query(
4466 context, share_id,
4467 ).filter_by(key=key).first()
4469 if not result:
4470 raise exception.MetadataItemNotFound()
4471 return result
4474############################
4475# Export locations functions
4476############################
4478def _export_location_get_all(
4479 context, share_instance_ids,
4480 include_admin_only=True,
4481 ignore_secondary_replicas=False,
4482):
4483 if not isinstance(share_instance_ids, (set, list, tuple)):
4484 share_instance_ids = (share_instance_ids, )
4486 query = model_query(
4487 context,
4488 models.ShareInstanceExportLocations,
4489 read_deleted="no",
4490 ).filter(
4491 models.ShareInstanceExportLocations.share_instance_id.in_(
4492 share_instance_ids),
4493 ).order_by(
4494 "updated_at",
4495 ).options(
4496 orm.joinedload(models.ShareInstanceExportLocations._el_metadata_bare),
4497 )
4499 if not include_admin_only:
4500 query = query.filter_by(is_admin_only=False)
4502 if ignore_secondary_replicas:
4503 replica_state_attr = models.ShareInstance.replica_state
4504 query = query.join(
4505 models.ShareInstanceExportLocations.share_instance,
4506 ).filter(
4507 or_(replica_state_attr == None, # noqa
4508 replica_state_attr == constants.REPLICA_STATE_ACTIVE))
4510 return query.all()
4513@require_context
4514@require_share_exists
4515@context_manager.reader
4516def export_location_get_all_by_share_id(
4517 context, share_id,
4518 include_admin_only=True,
4519 ignore_migration_destination=False,
4520 ignore_secondary_replicas=False,
4521):
4522 share = _share_get(context, share_id)
4523 if ignore_migration_destination:
4524 ids = [instance.id for instance in share.instances
4525 if instance['status'] != constants.STATUS_MIGRATING_TO]
4526 else:
4527 ids = [instance.id for instance in share.instances]
4528 rows = _export_location_get_all(
4529 context, ids, include_admin_only=include_admin_only,
4530 ignore_secondary_replicas=ignore_secondary_replicas)
4531 return rows
4534@require_context
4535@require_share_instance_exists
4536@context_manager.reader
4537def export_location_get_all_by_share_instance_id(
4538 context, share_instance_id, include_admin_only=True,
4539):
4540 rows = _export_location_get_all(
4541 context, [share_instance_id], include_admin_only=include_admin_only)
4542 return rows
4545@require_context
4546@require_share_exists
4547@context_manager.reader
4548def export_location_get_all(context, share_id):
4549 # NOTE(vponomaryov): this method is kept for compatibility with
4550 # old approach. New one uses 'export_location_get_all_by_share_id'.
4551 # Which returns list of dicts instead of list of strings, as this one does.
4552 share = _share_get(context, share_id)
4553 rows = _export_location_get_all(
4554 context, share.instance.id, context.is_admin)
4556 return [location['path'] for location in rows]
4559@require_context
4560@context_manager.reader
4561def export_location_get_by_uuid(
4562 context, export_location_uuid, ignore_secondary_replicas=False,
4563):
4564 return _export_location_get_by_uuid(
4565 context, export_location_uuid,
4566 ignore_secondary_replicas=ignore_secondary_replicas,
4567 )
4570def _export_location_get_by_uuid(
4571 context, export_location_uuid, ignore_secondary_replicas=False,
4572):
4573 query = model_query(
4574 context,
4575 models.ShareInstanceExportLocations,
4576 read_deleted="no",
4577 ).filter_by(
4578 uuid=export_location_uuid,
4579 ).options(
4580 orm.joinedload(models.ShareInstanceExportLocations._el_metadata_bare),
4581 )
4583 if ignore_secondary_replicas:
4584 replica_state_attr = models.ShareInstance.replica_state
4585 query = query.join(
4586 models.ShareInstanceExportLocations.share_instance,
4587 ).filter(
4588 or_(
4589 replica_state_attr == None, # noqa
4590 replica_state_attr == constants.REPLICA_STATE_ACTIVE,
4591 )
4592 )
4594 result = query.first()
4595 if not result:
4596 raise exception.ExportLocationNotFound(uuid=export_location_uuid)
4597 return result
4600@require_context
4601@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
4602@context_manager.writer
4603def export_locations_update(
4604 context, share_instance_id, export_locations, delete,
4605):
4606 return _export_locations_update(
4607 context, share_instance_id, export_locations, delete,
4608 )
4611def _export_locations_update(
4612 context, share_instance_id, export_locations, delete,
4613):
4614 # NOTE(u_glide):
4615 # Backward compatibility code for drivers,
4616 # which return single export_location as string
4617 if not isinstance(export_locations, (list, tuple, set)):
4618 export_locations = (export_locations, )
4620 export_locations_as_dicts = []
4621 for el in export_locations:
4622 # NOTE(vponomaryov): transform old export locations view to new one
4623 export_location = el
4624 if isinstance(el, str):
4625 export_location = {
4626 "path": el,
4627 "is_admin_only": False,
4628 "metadata": {},
4629 }
4630 elif isinstance(export_location, dict): 4630 ↛ 4634line 4630 didn't jump to line 4634 because the condition on line 4630 was always true
4631 if 'metadata' not in export_location:
4632 export_location['metadata'] = {}
4633 else:
4634 raise exception.ManilaException(
4635 _("Wrong export location type '%s'.") % type(export_location))
4636 export_locations_as_dicts.append(export_location)
4637 export_locations = export_locations_as_dicts
4639 export_locations_paths = [el['path'] for el in export_locations]
4641 current_el_rows = _export_location_get_all(context, share_instance_id)
4643 def get_path_list_from_rows(rows):
4644 return set([row['path'] for row in rows])
4646 current_el_paths = get_path_list_from_rows(current_el_rows)
4648 def create_indexed_time_dict(key_list):
4649 base = timeutils.utcnow()
4650 return {
4651 # NOTE(u_glide): Incrementing timestamp by microseconds to make
4652 # timestamp order match index order.
4653 key: base + datetime.timedelta(microseconds=index)
4654 for index, key in enumerate(key_list)
4655 }
4657 indexed_update_time = create_indexed_time_dict(export_locations_paths)
4659 for el in current_el_rows:
4660 if delete and el['path'] not in export_locations_paths:
4661 _export_location_metadata_delete(context, el['uuid'])
4662 el.soft_delete(session=context.session)
4663 else:
4664 updated_at = indexed_update_time[el['path']]
4665 el.update({
4666 'updated_at': updated_at,
4667 'deleted': 0,
4668 })
4669 el.save(session=context.session)
4671 new_export_metadata = next(
4672 exl.get('metadata', {})
4673 for exl in export_locations
4674 if exl['path'] == el['path']
4675 )
4676 new_export_metadata = new_export_metadata or el['el_metadata']
4678 if new_export_metadata:
4679 _export_location_metadata_update(
4680 context, el['uuid'], new_export_metadata,
4681 )
4683 # Now add new export locations
4684 for el in export_locations:
4685 if el['path'] in current_el_paths:
4686 # Already updated
4687 continue
4689 location_ref = models.ShareInstanceExportLocations()
4690 location_ref.update({
4691 'uuid': uuidutils.generate_uuid(),
4692 'path': el['path'],
4693 'share_instance_id': share_instance_id,
4694 'updated_at': indexed_update_time[el['path']],
4695 'deleted': 0,
4696 'is_admin_only': el.get('is_admin_only', False),
4697 })
4698 location_ref.save(session=context.session)
4699 if not el.get('metadata'):
4700 continue
4701 _export_location_metadata_update(
4702 context, location_ref['uuid'], el.get('metadata'),
4703 )
4705 return get_path_list_from_rows(
4706 _export_location_get_all(context, share_instance_id)
4707 )
4710#####################################
4711# Export locations metadata functions
4712#####################################
4714def _export_location_metadata_get_query(context, export_location_uuid):
4715 export_location_id = _export_location_get_by_uuid(
4716 context, export_location_uuid).id
4718 return model_query(
4719 context, models.ShareInstanceExportLocationsMetadata,
4720 read_deleted="no",
4721 ).filter_by(
4722 export_location_id=export_location_id,
4723 )
4726@require_context
4727@context_manager.reader
4728def export_location_metadata_get(context, export_location_uuid):
4729 return _export_location_metadata_get(context, export_location_uuid)
4732def _export_location_metadata_get(context, export_location_uuid):
4733 rows = _export_location_metadata_get_query(
4734 context, export_location_uuid,
4735 ).all()
4736 result = {}
4737 for row in rows:
4738 result[row["key"]] = row["value"]
4739 return result
4742@require_context
4743@context_manager.writer
4744def export_location_metadata_delete(context, export_location_uuid, keys=None):
4745 return _export_location_metadata_delete(
4746 context, export_location_uuid, keys=keys,
4747 )
4750def _export_location_metadata_delete(context, export_location_uuid, keys=None):
4751 metadata = _export_location_metadata_get_query(
4752 context, export_location_uuid,
4753 )
4754 # NOTE(vponomaryov): if keys is None then we delete all metadata.
4755 if keys is not None:
4756 keys = keys if isinstance(keys, (list, set, tuple)) else (keys, )
4757 metadata = metadata.filter(
4758 models.ShareInstanceExportLocationsMetadata.key.in_(keys),
4759 )
4760 metadata = metadata.all()
4761 for meta_ref in metadata:
4762 meta_ref.soft_delete(session=context.session)
4765@require_context
4766@context_manager.writer
4767def export_location_metadata_update(
4768 context, export_location_uuid, metadata, delete=False,
4769):
4770 return _export_location_metadata_update(
4771 context, export_location_uuid, metadata, delete=delete,
4772 )
4775@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
4776def _export_location_metadata_update(
4777 context, export_location_uuid, metadata, delete=False,
4778):
4779 if delete:
4780 original_metadata = _export_location_metadata_get(
4781 context, export_location_uuid,
4782 )
4783 keys_for_deletion = set(original_metadata).difference(metadata)
4784 if keys_for_deletion: 4784 ↛ 4789line 4784 didn't jump to line 4789 because the condition on line 4784 was always true
4785 _export_location_metadata_delete(
4786 context, export_location_uuid, keys=keys_for_deletion,
4787 )
4789 el = _export_location_get_by_uuid(context, export_location_uuid)
4790 for meta_key, meta_value in metadata.items():
4791 # NOTE(vponomaryov): we should use separate session
4792 # for each meta_ref because of autoincrement of integer primary key
4793 # that will not take effect using one session and we will rewrite,
4794 # in that case, single record - first one added with this call.
4795 context.session.commit()
4796 context.session.begin()
4798 if meta_value is None: 4798 ↛ 4799line 4798 didn't jump to line 4799 because the condition on line 4798 was never true
4799 LOG.warning("%s should be properly defined in the driver.",
4800 meta_key)
4802 item = {"value": meta_value, "updated_at": timeutils.utcnow()}
4804 meta_ref = _export_location_metadata_get_query(
4805 context, export_location_uuid,
4806 ).filter_by(
4807 key=meta_key,
4808 ).first()
4810 if not meta_ref:
4811 meta_ref = models.ShareInstanceExportLocationsMetadata()
4812 item.update({
4813 "key": meta_key,
4814 "export_location_id": el.id,
4815 })
4817 meta_ref.update(item)
4818 meta_ref.save(session=context.session)
4820 return metadata
4823@require_context
4824@context_manager.reader
4825def export_location_metadata_get_item(context, export_location_uuid, key):
4827 row = _export_location_metadata_get_item(
4828 context, export_location_uuid, key)
4829 result = {row['key']: row['value']}
4831 return result
4834@require_context
4835@context_manager.writer
4836def export_location_metadata_update_item(context, export_location_uuid,
4837 item):
4838 return _export_location_metadata_update(context, export_location_uuid,
4839 item, delete=False)
4842def _export_location_metadata_get_item(context, export_location_uuid, key):
4843 result = _export_location_metadata_get_query(
4844 context, export_location_uuid,
4845 ).filter_by(key=key).first()
4846 if not result:
4847 raise exception.MetadataItemNotFound()
4848 return result
4850###################################
4853def _security_service_get_query(context, project_only=False):
4854 return model_query(
4855 context, models.SecurityService, project_only=project_only,
4856 )
4859@require_context
4860@context_manager.writer
4861def security_service_create(context, values):
4862 values = ensure_model_dict_has_id(values)
4864 security_service_ref = models.SecurityService()
4865 security_service_ref.update(values)
4866 security_service_ref.save(session=context.session)
4868 return security_service_ref
4871@require_context
4872@context_manager.writer
4873def security_service_delete(context, id):
4874 security_service_ref = _security_service_get(context, id)
4875 security_service_ref.soft_delete(session=context.session)
4878@require_context
4879@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
4880@context_manager.writer
4881def security_service_update(context, id, values):
4882 security_service_ref = _security_service_get(context, id)
4883 security_service_ref.update(values)
4884 security_service_ref.save(session=context.session)
4885 return security_service_ref
4888@require_context
4889@context_manager.reader
4890def security_service_get(context, id, **kwargs):
4891 return _security_service_get(context, id, **kwargs)
4894@require_context
4895def _security_service_get(context, id, **kwargs):
4896 result = _security_service_get_query(
4897 context,
4898 **kwargs,
4899 ).filter_by(id=id).first()
4900 if result is None:
4901 raise exception.SecurityServiceNotFound(security_service_id=id)
4902 return result
4905@require_context
4906@context_manager.reader
4907def security_service_get_all(context):
4908 return _security_service_get_query(context).all()
4911@require_context
4912@context_manager.reader
4913def security_service_get_all_by_project(context, project_id):
4914 return _security_service_get_query(context).filter_by(
4915 project_id=project_id,
4916 ).all()
4919@require_context
4920@context_manager.reader
4921def security_service_get_all_by_share_network(context, share_network_id):
4922 return model_query(
4923 context, models.SecurityService,
4924 ).join(
4925 models.ShareNetworkSecurityServiceAssociation,
4926 models.SecurityService.id ==
4927 models.ShareNetworkSecurityServiceAssociation.security_service_id,
4928 ).filter_by(
4929 share_network_id=share_network_id, deleted=0,
4930 ).all()
4933###################
4936def _share_network_get_query(context):
4937 return model_query(
4938 context, models.ShareNetwork, project_only=True,
4939 ).options(
4940 orm.joinedload(models.ShareNetwork.share_instances),
4941 orm.joinedload(models.ShareNetwork.security_services),
4942 orm.subqueryload(models.ShareNetwork.share_network_subnets),
4943 )
4946@require_context
4947@context_manager.writer
4948def share_network_create(context, values):
4949 values = ensure_model_dict_has_id(values)
4951 network_ref = models.ShareNetwork()
4952 network_ref.update(values)
4953 network_ref.save(session=context.session)
4954 return _share_network_get(context, values['id'])
4957@require_context
4958@context_manager.writer
4959def share_network_delete(context, id):
4960 network_ref = _share_network_get(context, id)
4961 for subnet in network_ref['share_network_subnets']:
4962 share_network_subnet_delete(context, subnet['id'])
4963 network_ref.soft_delete(session=context.session)
4966@require_context
4967@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
4968@context_manager.writer
4969def share_network_update(context, id, values):
4970 network_ref = _share_network_get(context, id)
4971 network_ref.update(values)
4972 network_ref.save(session=context.session)
4973 return network_ref
4976@require_context
4977@context_manager.reader
4978def share_network_get(context, id):
4979 return _share_network_get(context, id)
4982@require_context
4983def _share_network_get(context, id):
4984 result = _share_network_get_query(context).filter_by(id=id).first()
4985 if result is None:
4986 raise exception.ShareNetworkNotFound(share_network_id=id)
4987 return result
4990@require_context
4991@context_manager.reader
4992def share_network_get_all_by_filter(context, filters=None):
4993 query = _share_network_get_query(context)
4995 legal_filter_keys = ('project_id', 'created_since', 'created_before')
4997 if not filters: 4997 ↛ 4998line 4997 didn't jump to line 4998 because the condition on line 4997 was never true
4998 filters = {}
5000 query = exact_filter(
5001 query, models.ShareNetwork, filters, legal_filter_keys,
5002 )
5003 if 'security_service_id' in filters: 5003 ↛ 5004line 5003 didn't jump to line 5004 because the condition on line 5003 was never true
5004 security_service_id = filters.get('security_service_id')
5005 query = query.join(
5006 models.ShareNetworkSecurityServiceAssociation,
5007 models.ShareNetwork.id == models.ShareNetworkSecurityServiceAssociation.share_network_id, # noqa: E501
5008 ).filter_by(
5009 security_service_id=security_service_id,
5010 deleted=0,
5011 )
5013 return query.all()
5016@require_context
5017@context_manager.reader
5018def share_network_get_all(context):
5019 return _share_network_get_query(context).all()
5022@require_context
5023@context_manager.reader
5024def share_network_get_all_by_project(context, project_id):
5025 return _share_network_get_query(
5026 context,
5027 ).filter_by(project_id=project_id).all()
5030@require_context
5031@context_manager.reader
5032def share_network_get_all_by_security_service(context, security_service_id):
5033 return model_query(
5034 context, models.ShareNetwork,
5035 ).join(
5036 models.ShareNetworkSecurityServiceAssociation,
5037 models.ShareNetwork.id ==
5038 models.ShareNetworkSecurityServiceAssociation.share_network_id,
5039 ).filter_by(security_service_id=security_service_id, deleted=0).all()
5042@require_context
5043@context_manager.writer
5044def share_network_add_security_service(context, id, security_service_id):
5045 assoc_ref = model_query(
5046 context,
5047 models.ShareNetworkSecurityServiceAssociation,
5048 ).filter_by(
5049 share_network_id=id,
5050 ).filter_by(security_service_id=security_service_id).first()
5052 if assoc_ref:
5053 msg = "Already associated"
5054 raise exception.ShareNetworkSecurityServiceAssociationError(
5055 share_network_id=id,
5056 security_service_id=security_service_id,
5057 reason=msg,
5058 )
5060 share_nw_ref = _share_network_get(context, id)
5061 security_service_ref = _security_service_get(context, security_service_id)
5062 share_nw_ref.security_services += [security_service_ref]
5063 share_nw_ref.save(session=context.session)
5065 return share_nw_ref
5068@require_context
5069@context_manager.reader
5070def share_network_security_service_association_get(
5071 context, share_network_id, security_service_id,
5072):
5073 association = model_query(
5074 context,
5075 models.ShareNetworkSecurityServiceAssociation,
5076 ).filter_by(
5077 share_network_id=share_network_id,
5078 ).filter_by(
5079 security_service_id=security_service_id,
5080 ).first()
5081 return association
5084@require_context
5085@context_manager.writer
5086def share_network_remove_security_service(context, id, security_service_id):
5087 share_nw_ref = _share_network_get(context, id)
5088 _security_service_get(context, security_service_id)
5090 assoc_ref = model_query(
5091 context,
5092 models.ShareNetworkSecurityServiceAssociation,
5093 ).filter_by(
5094 share_network_id=id,
5095 ).filter_by(security_service_id=security_service_id).first()
5097 if assoc_ref:
5098 assoc_ref.soft_delete(session=context.session)
5099 else:
5100 msg = "No association defined"
5101 raise exception.ShareNetworkSecurityServiceDissociationError(
5102 share_network_id=id,
5103 security_service_id=security_service_id,
5104 reason=msg,
5105 )
5107 return share_nw_ref
5110@require_context
5111@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
5112@context_manager.writer
5113def share_network_update_security_service(
5114 context, id, current_security_service_id, new_security_service_id,
5115):
5116 share_nw_ref = _share_network_get(context, id)
5117 # Check if the old security service exists
5118 _security_service_get(context, current_security_service_id)
5119 new_security_service_ref = _security_service_get(
5120 context, new_security_service_id,
5121 )
5123 assoc_ref = model_query(
5124 context,
5125 models.ShareNetworkSecurityServiceAssociation,
5126 ).filter_by(
5127 share_network_id=id,
5128 ).filter_by(
5129 security_service_id=current_security_service_id,
5130 ).first()
5132 if assoc_ref: 5132 ↛ 5135line 5132 didn't jump to line 5135 because the condition on line 5132 was always true
5133 assoc_ref.soft_delete(session=context.session)
5134 else:
5135 msg = "No association defined"
5136 raise exception.ShareNetworkSecurityServiceDissociationError(
5137 share_network_id=id,
5138 security_service_id=current_security_service_id,
5139 reason=msg)
5141 # Add new association
5142 share_nw_ref.security_services += [new_security_service_ref]
5143 share_nw_ref.save(session=context.session)
5145 return share_nw_ref
5148@require_context
5149def _count_share_networks(
5150 context, project_id, user_id=None, share_type_id=None,
5151):
5152 query = model_query(
5153 context, models.ShareNetwork,
5154 func.count(models.ShareNetwork.id),
5155 read_deleted="no",
5156 ).filter_by(project_id=project_id)
5157 if share_type_id: 5157 ↛ 5158line 5157 didn't jump to line 5158 because the condition on line 5157 was never true
5158 query = query.join(
5159 models.ShareNetwork.share_instances,
5160 ).filter_by(share_type_id=share_type_id)
5161 elif user_id is not None: 5161 ↛ 5163line 5161 didn't jump to line 5163 because the condition on line 5161 was always true
5162 query = query.filter_by(user_id=user_id)
5163 return query.first()[0]
5166###################
5169@require_context
5170def _share_network_subnet_get_query(context):
5171 return model_query(
5172 context, models.ShareNetworkSubnet,
5173 ).options(
5174 orm.joinedload(models.ShareNetworkSubnet.share_servers),
5175 orm.joinedload(models.ShareNetworkSubnet.share_network),
5176 orm.joinedload(
5177 models.ShareNetworkSubnet.share_network_subnet_metadata
5178 ),
5179 )
5182@require_context
5183@context_manager.writer
5184def share_network_subnet_create(context, values):
5185 values = ensure_model_dict_has_id(values)
5186 values['share_network_subnet_metadata'] = _metadata_refs(
5187 values.pop('metadata', {}), models.ShareNetworkSubnetMetadata)
5189 network_subnet_ref = models.ShareNetworkSubnet()
5190 network_subnet_ref.update(values)
5191 network_subnet_ref.save(session=context.session)
5192 return _share_network_subnet_get(
5193 context, network_subnet_ref['id'],
5194 )
5197@require_context
5198@context_manager.writer
5199def share_network_subnet_delete(context, network_subnet_id):
5200 network_subnet_ref = _share_network_subnet_get(context, network_subnet_id)
5201 context.session.query(models.ShareNetworkSubnetMetadata).filter_by(
5202 share_network_subnet_id=network_subnet_id,
5203 ).soft_delete()
5204 network_subnet_ref.soft_delete(session=context.session, update_status=True)
5207@require_context
5208@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
5209@context_manager.writer
5210def share_network_subnet_update(context, network_subnet_id, values):
5211 network_subnet_ref = _share_network_subnet_get(context, network_subnet_id)
5212 network_subnet_ref.update(values)
5213 network_subnet_ref.save(session=context.session)
5214 return network_subnet_ref
5217@require_context
5218@context_manager.reader
5219def share_network_subnet_get(context, network_subnet_id, parent_id=None):
5220 return _share_network_subnet_get(
5221 context, network_subnet_id, parent_id=parent_id,
5222 )
5225@require_context
5226def _share_network_subnet_get(context, network_subnet_id, parent_id=None):
5227 kwargs = {'id': network_subnet_id}
5228 if parent_id: 5228 ↛ 5229line 5228 didn't jump to line 5229 because the condition on line 5228 was never true
5229 kwargs['share_network_id'] = parent_id
5230 result = _share_network_subnet_get_query(
5231 context,
5232 ).filter_by(**kwargs).first()
5233 if result is None:
5234 raise exception.ShareNetworkSubnetNotFound(
5235 share_network_subnet_id=network_subnet_id,
5236 )
5237 return result
5240@require_context
5241@context_manager.reader
5242def share_network_subnet_get_all_with_same_az(context, network_subnet_id):
5243 subnet = _share_network_subnet_get_query(
5244 context,
5245 ).filter_by(id=network_subnet_id).subquery()
5246 result = _share_network_subnet_get_query(
5247 context,
5248 ).join(
5249 subnet,
5250 subnet.c.share_network_id ==
5251 models.ShareNetworkSubnet.share_network_id,
5252 ).filter(
5253 func.coalesce(subnet.c.availability_zone_id, '0') ==
5254 func.coalesce(models.ShareNetworkSubnet.availability_zone_id, '0')
5255 ).all()
5256 if not result:
5257 raise exception.ShareNetworkSubnetNotFound(
5258 share_network_subnet_id=network_subnet_id,
5259 )
5260 return result
5263@require_context
5264@context_manager.reader
5265def share_network_subnet_get_all(context):
5266 return _share_network_subnet_get_query(context).all()
5269@require_context
5270@context_manager.reader
5271def share_network_subnet_get_all_by_share_network(context, network_id):
5272 return _share_network_subnet_get_query(context).filter_by(
5273 share_network_id=network_id,
5274 ).all()
5277@require_context
5278@context_manager.reader
5279def share_network_subnets_get_all_by_availability_zone_id(
5280 context, share_network_id, availability_zone_id,
5281 fallback_to_default=True,
5282):
5283 """Get the share network subnets DB records in a given AZ.
5285 This method returns list of subnets DB record for a given share network id
5286 and an availability zone. If the 'availability_zone_id' is 'None', a
5287 record may be returned and it will represent the default share network
5288 subnets. If there is no subnet for a specific availability zone id and
5289 "fallback_to_default" is True, this method will return the default share
5290 network subnets, if it exists.
5292 :param context: operation context.
5293 :param share_network_id: the share network id to be the subnets.
5294 :param availability_zone_id: the availability zone id to be the subnets.
5295 :param fallback_to_default: determines in case no subnets found in the
5296 given AZ, it will return the "default" subnets.
5297 :return: the list of share network subnets in the AZ and share network.
5298 """
5299 return _share_network_subnets_get_all_by_availability_zone_id(
5300 context, share_network_id, availability_zone_id,
5301 fallback_to_default=fallback_to_default,
5302 )
5305@require_context
5306def _share_network_subnets_get_all_by_availability_zone_id(
5307 context, share_network_id, availability_zone_id,
5308 fallback_to_default=True,
5309):
5310 result = _share_network_subnet_get_query(context).filter_by(
5311 share_network_id=share_network_id,
5312 availability_zone_id=availability_zone_id,
5313 ).all()
5314 # If a specific subnet wasn't found, try get the default one
5315 if availability_zone_id and not result and fallback_to_default:
5316 return _share_network_subnet_get_query(context).filter_by(
5317 share_network_id=share_network_id,
5318 availability_zone_id=None,
5319 ).all()
5320 return result
5323@require_context
5324@context_manager.reader
5325def share_network_subnet_get_default_subnets(context, share_network_id):
5326 return _share_network_subnets_get_all_by_availability_zone_id(
5327 context, share_network_id, availability_zone_id=None,
5328 )
5331@require_context
5332@context_manager.reader
5333def share_network_subnet_get_all_by_share_server_id(context, share_server_id):
5334 result = _share_network_subnet_get_query(context).filter(
5335 models.ShareNetworkSubnet.share_servers.any(
5336 id=share_server_id,
5337 )
5338 ).all()
5339 if not result:
5340 raise exception.ShareNetworkSubnetNotFoundByShareServer(
5341 share_server_id=share_server_id,
5342 )
5344 return result
5346###################
5349def _share_network_subnet_metadata_get_query(context, share_network_subnet_id):
5350 return model_query(
5351 context, models.ShareNetworkSubnetMetadata,
5352 read_deleted="no",
5353 ).filter_by(
5354 share_network_subnet_id=share_network_subnet_id,
5355 ).options(
5356 orm.joinedload(models.ShareNetworkSubnetMetadata.share_network_subnet),
5357 )
5360@require_context
5361@require_share_network_subnet_exists
5362@context_manager.reader
5363def share_network_subnet_metadata_get(context, share_network_subnet_id):
5364 return _share_network_subnet_metadata_get(context, share_network_subnet_id)
5367@require_context
5368def _share_network_subnet_metadata_get(context, share_network_subnet_id):
5369 rows = _share_network_subnet_metadata_get_query(
5370 context, share_network_subnet_id,
5371 ).all()
5373 result = {}
5374 for row in rows:
5375 result[row['key']] = row['value']
5376 return result
5379@require_context
5380@require_share_network_subnet_exists
5381@context_manager.writer
5382def share_network_subnet_metadata_delete(
5383 context, share_network_subnet_id, key,
5384):
5385 meta_ref = _share_network_subnet_metadata_get_item(
5386 context, share_network_subnet_id, key,
5387 )
5388 meta_ref.soft_delete(session=context.session)
5391@require_context
5392@require_share_network_subnet_exists
5393@context_manager.writer
5394def share_network_subnet_metadata_update(
5395 context, share_network_subnet_id, metadata, delete,
5396):
5397 return _share_network_subnet_metadata_update(
5398 context, share_network_subnet_id, metadata, delete,
5399 )
5402@require_context
5403@context_manager.writer
5404def share_network_subnet_metadata_update_item(
5405 context, share_network_subnet_id, item,
5406):
5407 return _share_network_subnet_metadata_update(
5408 context, share_network_subnet_id, item, delete=False,
5409 )
5412@require_context
5413@context_manager.reader
5414def share_network_subnet_metadata_get_item(
5415 context, share_network_subnet_id, key,
5416):
5417 row = _share_network_subnet_metadata_get_item(
5418 context, share_network_subnet_id, key,
5419 )
5420 result = {row['key']: row['value']}
5421 return result
5424def _share_network_subnet_metadata_get_item(
5425 context, share_network_subnet_id, key,
5426):
5427 result = _share_network_subnet_metadata_get_query(
5428 context, share_network_subnet_id,
5429 ).filter_by(key=key).first()
5430 if not result: 5430 ↛ 5431line 5430 didn't jump to line 5431 because the condition on line 5430 was never true
5431 raise exception.MetadataItemNotFound
5432 return result
5435@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
5436def _share_network_subnet_metadata_update(
5437 context, share_network_subnet_id, metadata, delete,
5438):
5439 delete = strutils.bool_from_string(delete)
5440 if delete: 5440 ↛ 5441line 5440 didn't jump to line 5441 because the condition on line 5440 was never true
5441 original_metadata = _share_network_subnet_metadata_get(
5442 context, share_network_subnet_id,
5443 )
5444 for meta_key, meta_value in original_metadata.items():
5445 if meta_key not in metadata:
5446 meta_ref = _share_network_subnet_metadata_get_item(
5447 context, share_network_subnet_id, meta_key,
5448 )
5449 meta_ref.soft_delete(session=context.session)
5450 meta_ref = None
5451 # Now update all existing items with new values, or create new meta
5452 # objects.
5453 for meta_key, meta_value in metadata.items():
5455 # update the value whether it exists or not.
5456 item = {"value": meta_value}
5457 meta_ref = _share_network_subnet_metadata_get_query(
5458 context, share_network_subnet_id,
5459 ).filter_by(key=meta_key).first()
5460 if not meta_ref:
5461 meta_ref = models.ShareNetworkSubnetMetadata()
5462 item.update(
5463 {
5464 "key": meta_key,
5465 "share_network_subnet_id": share_network_subnet_id,
5466 }
5467 )
5468 meta_ref.update(item)
5469 meta_ref.save(session=context.session)
5471 return metadata
5473#################################
5476def _share_server_get_query(context):
5477 return model_query(
5478 context, models.ShareServer,
5479 ).options(
5480 orm.joinedload(models.ShareServer.share_instances),
5481 orm.joinedload(models.ShareServer.network_allocations),
5482 orm.joinedload(models.ShareServer.share_network_subnets),
5483 )
5486@require_context
5487@context_manager.writer
5488def share_server_create(context, values):
5489 values = ensure_model_dict_has_id(values)
5491 server_ref = models.ShareServer()
5492 # updated_at is needed for judgement of automatic cleanup
5493 server_ref.updated_at = timeutils.utcnow()
5494 server_ref.update(values)
5496 # If encryption_key_ref is present, create associated record
5497 encryption_key_ref = values.get('encryption_key_ref')
5498 if encryption_key_ref:
5499 encryption_ref = models.EncryptionRef(
5500 id=uuidutils.generate_uuid(),
5501 share_server_id=server_ref['id'],
5502 encryption_key_ref=encryption_key_ref,
5503 project_id=context.project_id,
5504 )
5505 server_ref.server_encryption_ref_entry = encryption_ref
5507 server_ref.save(session=context.session)
5509 # NOTE(u_glide): Do so to prevent errors with relationships
5510 return _share_server_get(context, server_ref['id'])
5513@require_context
5514@context_manager.writer
5515def share_server_delete(context, id):
5516 server_ref = _share_server_get(context, id)
5517 model_query(
5518 context, models.ShareServerShareNetworkSubnetMapping,
5519 ).filter_by(
5520 share_server_id=id,
5521 ).soft_delete()
5522 _share_server_backend_details_delete(context, id)
5523 server_ref.soft_delete(session=context.session, update_status=True)
5525 # If encryption_key_ref is present, delete associated encryption ref entry
5526 if server_ref.server_encryption_ref_entry: 5526 ↛ 5527line 5526 didn't jump to line 5527 because the condition on line 5526 was never true
5527 server_ref.server_encryption_ref_entry.soft_delete(
5528 session=context.session)
5531@require_context
5532@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
5533@context_manager.writer
5534def share_server_update(context, id, values):
5535 server_ref = _share_server_get(context, id)
5536 server_ref.update(values)
5537 server_ref.save(session=context.session)
5538 return server_ref
5541@require_context
5542@context_manager.reader
5543def share_server_get(context, server_id):
5544 return _share_server_get(context, server_id)
5547@require_context
5548def _share_server_get(context, server_id):
5549 result = _share_server_get_query(context).filter_by(id=server_id).first()
5550 if result is None:
5551 raise exception.ShareServerNotFound(share_server_id=server_id)
5552 return result
5555@require_context
5556@context_manager.reader
5557def share_server_search_by_identifier(context, identifier):
5559 identifier_field = models.ShareServer.identifier
5561 # try if given identifier is a suffix of existing entry's identifier
5562 result = (_share_server_get_query(context).filter(
5563 identifier_field.like('%{}'.format(identifier))).all())
5565 if not result:
5566 # repeat it with underscores instead of hyphens
5567 result = (_share_server_get_query(context).filter(
5568 identifier_field.like('%{}'.format(
5569 identifier.replace("-", "_")))).all())
5571 if not result:
5572 # repeat it with hypens instead of underscores
5573 result = (_share_server_get_query(context).filter(
5574 identifier_field.like('%{}'.format(
5575 identifier.replace("_", "-")))).all())
5577 if not result:
5578 # try if an existing identifier is a substring of given identifier
5579 result = (_share_server_get_query(context).filter(
5580 literal(identifier).contains(identifier_field)).all())
5582 if not result:
5583 # repeat it with underscores instead of hyphens
5584 result = (_share_server_get_query(context).filter(
5585 literal(identifier.replace("-", "_")).contains(
5586 identifier_field)).all())
5588 if not result:
5589 # repeat it with hypens instead of underscores
5590 result = (_share_server_get_query(context).filter(
5591 literal(identifier.replace("_", "-")).contains(
5592 identifier_field)).all())
5594 if not result:
5595 raise exception.ShareServerNotFound(share_server_id=identifier)
5597 return result
5600@require_context
5601@context_manager.reader
5602def share_server_get_all_by_host_and_share_subnet_valid(
5603 context, host, share_subnet_id,
5604):
5605 result = _share_server_get_query(
5606 context,
5607 ).filter_by(
5608 host=host,
5609 ).filter(
5610 models.ShareServer.share_network_subnets.any(id=share_subnet_id)
5611 ).filter(
5612 models.ShareServer.status.in_(
5613 (constants.STATUS_CREATING, constants.STATUS_ACTIVE),
5614 )
5615 ).all()
5617 if not result:
5618 filters_description = ('share_network_subnet_id is '
5619 '"%(share_subnet_id)s", host is "%(host)s" and '
5620 'status in "%(status_cr)s" or '
5621 '"%(status_act)s"') % {
5622 'share_subnet_id': share_subnet_id,
5623 'host': host,
5624 'status_cr': constants.STATUS_CREATING,
5625 'status_act': constants.STATUS_ACTIVE,
5626 }
5627 raise exception.ShareServerNotFoundByFilters(
5628 filters_description=filters_description,
5629 )
5630 return result
5633@require_context
5634@context_manager.reader
5635def share_server_get_all_by_host_and_or_share_subnet(
5636 context, host=None, share_subnet_id=None,
5637):
5638 result = _share_server_get_query(context)
5639 if host: 5639 ↛ 5641line 5639 didn't jump to line 5641 because the condition on line 5639 was always true
5640 result = result.filter_by(host=host)
5641 result = result.filter(
5642 models.ShareServer.share_network_subnets.any(id=share_subnet_id)
5643 ).all()
5645 if not result:
5646 filters_description = (
5647 'share_network_subnet_id is "%(share_subnet_id)s" and host is '
5648 '"%(host)s".'
5649 ) % {
5650 'share_subnet_id': share_subnet_id,
5651 'host': host,
5652 }
5653 raise exception.ShareServerNotFoundByFilters(
5654 filters_description=filters_description,
5655 )
5656 return result
5659@require_context
5660@context_manager.reader
5661def share_server_get_all(context):
5662 return _share_server_get_query(context).all()
5665@require_context
5666@context_manager.reader
5667def share_server_get_all_with_filters(context, filters):
5668 return _share_server_get_all_with_filters(context, filters)
5671@require_context
5672def _share_server_get_all_with_filters(context, filters):
5673 query = _share_server_get_query(context)
5675 if filters.get('host'):
5676 query = query.filter_by(host=filters.get('host'))
5677 if filters.get('status'):
5678 query = query.filter_by(status=filters.get('status'))
5679 if filters.get('source_share_server_id'):
5680 query = query.filter_by(
5681 source_share_server_id=filters.get('source_share_server_id'))
5682 if filters.get('encryption_key_ref'): 5682 ↛ 5683line 5682 didn't jump to line 5683 because the condition on line 5682 was never true
5683 query = query.filter_by(
5684 encryption_key_ref=filters.get('encryption_key_ref'))
5685 if filters.get('identifier'): 5685 ↛ 5686line 5685 didn't jump to line 5686 because the condition on line 5685 was never true
5686 query = query.filter_by(identifier=filters.get('identifier'))
5687 if filters.get('share_network_id'):
5688 query = query.join(
5689 models.ShareServerShareNetworkSubnetMapping,
5690 models.ShareServerShareNetworkSubnetMapping.share_server_id ==
5691 models.ShareServer.id
5692 ).join(
5693 models.ShareNetworkSubnet,
5694 models.ShareNetworkSubnet.id ==
5695 models.ShareServerShareNetworkSubnetMapping.share_network_subnet_id
5696 ).filter(
5697 models.ShareNetworkSubnet.share_network_id ==
5698 filters.get('share_network_id'))
5699 return query.all()
5702@require_context
5703@context_manager.reader
5704def share_server_get_all_by_host(context, host, filters=None):
5705 if filters: 5705 ↛ 5706line 5705 didn't jump to line 5706 because the condition on line 5705 was never true
5706 filters.update({'host': host})
5707 else:
5708 filters = {'host': host}
5709 return _share_server_get_all_with_filters(context, filters=filters)
5712@require_context
5713@context_manager.reader
5714def share_server_get_all_unused_deletable(context, host, updated_before):
5715 valid_server_status = (
5716 constants.STATUS_INACTIVE,
5717 constants.STATUS_ACTIVE,
5718 constants.STATUS_ERROR,
5719 )
5720 result = (_share_server_get_query(context)
5721 .filter_by(is_auto_deletable=True)
5722 .filter_by(host=host)
5723 .filter(~models.ShareServer.share_groups.any())
5724 .filter(~models.ShareServer.share_instances.any())
5725 .filter(models.ShareServer.status.in_(valid_server_status))
5726 .filter(models.ShareServer.updated_at < updated_before).all())
5727 return result
5730def _share_server_backend_details_get_item(context, share_server_id, key):
5731 result = _share_server_backend_details_get_query(
5732 context, share_server_id,
5733 ).filter_by(key=key).first()
5734 if not result:
5735 raise exception.ShareServerBackendDetailsNotFound()
5736 return result
5739def _share_server_backend_details_get_query(context, share_server_id):
5740 return (model_query(
5741 context, models.ShareServerBackendDetails,
5742 read_deleted="no").
5743 filter_by(share_server_id=share_server_id))
5746@require_context
5747@context_manager.writer
5748def share_server_backend_details_set(context, share_server_id, server_details):
5749 _share_server_get(context, share_server_id)
5751 for meta_key, meta_value in server_details.items():
5753 # update the value whether it exists or not
5754 item = {"value": meta_value}
5755 try:
5756 meta_ref = _share_server_backend_details_get_item(
5757 context, share_server_id, meta_key)
5758 except exception.ShareServerBackendDetailsNotFound:
5759 meta_ref = models.ShareServerBackendDetails()
5760 item.update({"key": meta_key, "share_server_id": share_server_id})
5762 meta_ref.update(item)
5763 meta_ref.save(session=context.session)
5764 return server_details
5767@require_context
5768@context_manager.reader
5769def share_server_backend_details_get_item(context, share_server_id, meta_key):
5770 try:
5771 meta_ref = _share_server_backend_details_get_item(
5772 context, share_server_id, meta_key)
5773 except exception.ShareServerBackendDetailsNotFound:
5774 return None
5775 return meta_ref.get('value')
5778@require_context
5779@context_manager.writer
5780def share_server_backend_details_delete(context, share_server_id):
5781 return _share_server_backend_details_delete(context, share_server_id)
5784@require_context
5785def _share_server_backend_details_delete(context, share_server_id):
5786 share_server_details = model_query(
5787 context,
5788 models.ShareServerBackendDetails,
5789 ).filter_by(share_server_id=share_server_id).all()
5790 for item in share_server_details:
5791 item.soft_delete(session=context.session)
5794@require_context
5795@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
5796@context_manager.writer
5797def share_servers_update(context, share_server_ids, values):
5798 result = model_query(
5799 context, models.ShareServer, read_deleted="no",
5800 ).filter(
5801 models.ShareServer.id.in_(share_server_ids),
5802 ).update(values, synchronize_session=False)
5803 return result
5806###################
5808def _driver_private_data_query(
5809 context, entity_id, key=None, read_deleted=False,
5810):
5811 query = model_query(
5812 context, models.DriverPrivateData,
5813 read_deleted=read_deleted,
5814 ).filter_by(
5815 entity_uuid=entity_id,
5816 )
5818 if isinstance(key, list):
5819 return query.filter(models.DriverPrivateData.key.in_(key))
5820 elif key is not None:
5821 return query.filter_by(key=key)
5823 return query
5826@require_context
5827@context_manager.reader
5828def driver_private_data_get(context, entity_id, key=None, default=None):
5829 query = _driver_private_data_query(context, entity_id, key)
5831 if key is None or isinstance(key, list):
5832 return {item.key: item.value for item in query.all()}
5833 else:
5834 result = query.first()
5835 return result["value"] if result is not None else default
5838@require_context
5839@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
5840@context_manager.writer
5841def driver_private_data_update(
5842 context, entity_id, details, delete_existing=False,
5843):
5844 # NOTE(u_glide): following code modifies details dict, that's why we should
5845 # copy it
5846 new_details = copy.deepcopy(details)
5848 # Process existing data
5849 original_data = context.session.query(models.DriverPrivateData).filter_by(
5850 entity_uuid=entity_id,
5851 ).all()
5853 for data_ref in original_data:
5854 in_new_details = data_ref['key'] in new_details
5856 if in_new_details:
5857 new_value = str(new_details.pop(data_ref['key']))
5858 data_ref.update({
5859 "value": new_value,
5860 "deleted": 0,
5861 "deleted_at": None
5862 })
5863 data_ref.save(session=context.session)
5864 elif delete_existing and data_ref['deleted'] != 1:
5865 data_ref.update({
5866 "deleted": 1, "deleted_at": timeutils.utcnow()
5867 })
5868 data_ref.save(session=context.session)
5870 # Add new data
5871 for key, value in new_details.items():
5872 data_ref = models.DriverPrivateData()
5873 data_ref.update({
5874 "entity_uuid": entity_id,
5875 "key": key,
5876 "value": str(value)
5877 })
5878 data_ref.save(session=context.session)
5880 return details
5883@require_context
5884@context_manager.writer
5885def driver_private_data_delete(context, entity_id, key=None):
5886 query = _driver_private_data_query(context, entity_id, key)
5887 query.update({"deleted": 1, "deleted_at": timeutils.utcnow()})
5890###################
5893@require_context
5894@context_manager.writer
5895def network_allocation_create(context, values):
5896 values = ensure_model_dict_has_id(values)
5897 alloc_ref = models.NetworkAllocation()
5898 alloc_ref.update(values)
5899 alloc_ref.save(session=context.session)
5900 return alloc_ref
5903@require_context
5904@context_manager.writer
5905def network_allocation_delete(context, id):
5906 alloc_ref = _network_allocation_get(context, id)
5907 alloc_ref.soft_delete(session=context.session)
5910@require_context
5911@context_manager.reader
5912def network_allocation_get(context, id, read_deleted="no"):
5913 return _network_allocation_get(context, id, read_deleted=read_deleted)
5916@require_context
5917def _network_allocation_get(context, id, read_deleted="no"):
5918 result = model_query(
5919 context, models.NetworkAllocation,
5920 read_deleted=read_deleted,
5921 ).filter_by(id=id).first()
5922 if result is None:
5923 raise exception.NotFound()
5924 return result
5927@require_context
5928@context_manager.reader
5929def network_allocations_get_by_ip_address(context, ip_address):
5930 result = model_query(
5931 context, models.NetworkAllocation,
5932 ).filter_by(ip_address=ip_address).all()
5933 return result or []
5936@require_context
5937@context_manager.reader
5938def network_allocations_get_for_share_server(
5939 context, share_server_id, label=None, subnet_id=None,
5940):
5941 query = model_query(
5942 context, models.NetworkAllocation,
5943 ).filter_by(
5944 share_server_id=share_server_id,
5945 )
5946 if label:
5947 if label == 'user':
5948 query = query.filter(or_(
5949 # NOTE(vponomaryov): we treat None as alias for 'user'.
5950 models.NetworkAllocation.label == None, # noqa
5951 models.NetworkAllocation.label == label,
5952 ))
5953 else:
5954 query = query.filter(models.NetworkAllocation.label == label)
5955 if subnet_id:
5956 query = query.filter(
5957 models.NetworkAllocation.share_network_subnet_id == subnet_id)
5959 result = query.all()
5960 return result
5963@require_context
5964@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
5965@context_manager.writer
5966def network_allocation_update(context, id, values, read_deleted=None):
5967 alloc_ref = _network_allocation_get(context, id, read_deleted=read_deleted)
5968 alloc_ref.update(values)
5969 alloc_ref.save(session=context.session)
5970 return alloc_ref
5973###################
5976def _dict_with_specs(inst_type_query, specs_key='extra_specs'):
5977 """Convert type query result to dict with extra_spec and rate_limit.
5979 Takes a share [group] type query returned by sqlalchemy and returns it
5980 as a dictionary, converting the extra/group specs entry from a list
5981 of dicts:
5983 'extra_specs' : [{'key': 'k1', 'value': 'v1', ...}, ...]
5984 'group_specs' : [{'key': 'k1', 'value': 'v1', ...}, ...]
5985 to a single dict:
5986 'extra_specs' : {'k1': 'v1'}
5987 'group_specs' : {'k1': 'v1'}
5988 """
5989 inst_type_dict = dict(inst_type_query)
5990 specs = {x['key']: x['value'] for x in inst_type_query[specs_key]}
5991 inst_type_dict[specs_key] = specs
5992 return inst_type_dict
5995@require_admin_context
5996@context_manager.writer
5997def share_type_create(context, values, projects=None):
5998 """Create a new share type.
6000 In order to pass in extra specs, the values dict should contain a
6001 'extra_specs' key/value pair:
6002 {'extra_specs' : {'k1': 'v1', 'k2': 'v2', ...}}
6003 """
6004 values = ensure_model_dict_has_id(values)
6006 projects = projects or []
6008 try:
6009 values['extra_specs'] = _metadata_refs(
6010 values.get('extra_specs'),
6011 models.ShareTypeExtraSpecs,
6012 )
6013 share_type_ref = models.ShareTypes()
6014 share_type_ref.update(values)
6015 share_type_ref.save(session=context.session)
6016 except db_exception.DBDuplicateEntry:
6017 raise exception.ShareTypeExists(id=values['name'])
6018 except Exception as e:
6019 raise db_exception.DBError(e)
6021 for project in set(projects):
6022 access_ref = models.ShareTypeProjects()
6023 access_ref.update(
6024 {"share_type_id": share_type_ref.id, "project_id": project},
6025 )
6026 access_ref.save(session=context.session)
6028 return share_type_ref
6031def _share_type_get_query(context, read_deleted=None, expected_fields=None):
6032 expected_fields = expected_fields or []
6033 query = model_query(
6034 context,
6035 models.ShareTypes,
6036 read_deleted=read_deleted,
6037 ).options(orm.joinedload(models.ShareTypes.extra_specs))
6039 if 'projects' in expected_fields: 6039 ↛ 6040line 6039 didn't jump to line 6040 because the condition on line 6039 was never true
6040 query = query.options(orm.joinedload(models.ShareTypes.projects))
6042 if not context.is_admin:
6043 the_filter = [models.ShareTypes.is_public == true()]
6044 projects_attr = getattr(models.ShareTypes, 'projects')
6045 the_filter.extend([
6046 projects_attr.any(project_id=context.project_id)
6047 ])
6048 query = query.filter(or_(*the_filter))
6050 return query
6053@handle_db_data_error
6054@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
6055def _share_type_update(context, type_id, values, is_group):
6057 if values.get('name') is None:
6058 values.pop('name', None)
6060 if is_group: 6060 ↛ 6061line 6060 didn't jump to line 6061 because the condition on line 6060 was never true
6061 model = models.ShareGroupTypes
6062 exists_exc = exception.ShareGroupTypeExists
6063 exists_args = {'type_id': values.get('name')}
6064 else:
6065 model = models.ShareTypes
6066 exists_exc = exception.ShareTypeExists
6067 exists_args = {'id': values.get('name')}
6069 query = model_query(context, model)
6071 try:
6072 result = query.filter_by(id=type_id).update(values)
6073 except db_exception.DBDuplicateEntry:
6074 # This exception only occurs if there's a non-deleted
6075 # share/group type which has the same name as the name being
6076 # updated.
6077 raise exists_exc(**exists_args)
6079 if not result:
6080 if is_group: 6080 ↛ 6081line 6080 didn't jump to line 6081 because the condition on line 6080 was never true
6081 raise exception.ShareGroupTypeNotFound(type_id=type_id)
6082 else:
6083 raise exception.ShareTypeNotFound(share_type_id=type_id)
6086@context_manager.writer
6087def share_type_update(context, share_type_id, values):
6088 _share_type_update(context, share_type_id, values, is_group=False)
6091@require_context
6092@context_manager.reader
6093def share_type_get_all(context, inactive=False, filters=None):
6094 """Returns a dict describing all share_types with name as key."""
6095 filters = filters or {}
6097 read_deleted = "yes" if inactive else "no"
6099 query = _share_type_get_query(context, read_deleted=read_deleted)
6101 if 'is_public' in filters and filters['is_public'] is not None:
6102 the_filter = [models. ShareTypes.is_public == filters['is_public']]
6103 if filters['is_public'] and context.project_id is not None:
6104 projects_attr = getattr(models. ShareTypes, 'projects')
6105 the_filter.extend([
6106 projects_attr.any(
6107 project_id=context.project_id, deleted=0)
6108 ])
6109 if len(the_filter) > 1:
6110 query = query.filter(or_(*the_filter))
6111 else:
6112 query = query.filter(the_filter[0])
6114 rows = query.order_by("name").all()
6116 result = {}
6117 for row in rows:
6118 result[row['name']] = _dict_with_specs(row)
6120 return result
6123def _share_type_get_id_from_share_type(context, id):
6124 result = model_query(
6125 context, models.ShareTypes, read_deleted="no",
6126 ).filter_by(id=id).first()
6127 if not result:
6128 raise exception.ShareTypeNotFound(share_type_id=id)
6129 return result['id']
6132def _share_type_get(context, id, inactive=False, expected_fields=None):
6133 expected_fields = expected_fields or []
6134 read_deleted = "yes" if inactive else "no"
6135 result = _share_type_get_query(
6136 context, read_deleted, expected_fields,
6137 ).filter_by(id=id).first()
6139 if not result:
6140 # The only way that id could be None is if the default share type is
6141 # not configured and no other share type was specified.
6142 if id is None:
6143 raise exception.DefaultShareTypeNotConfigured()
6144 raise exception.ShareTypeNotFound(share_type_id=id)
6146 share_type = _dict_with_specs(result)
6148 if 'projects' in expected_fields: 6148 ↛ 6149line 6148 didn't jump to line 6149 because the condition on line 6148 was never true
6149 share_type['projects'] = [p['project_id'] for p in result['projects']]
6151 return share_type
6154@require_context
6155@context_manager.reader
6156def share_type_get(context, id, inactive=False, expected_fields=None):
6157 """Return a dict describing specific share_type."""
6158 return _share_type_get(context, id,
6159 inactive=inactive,
6160 expected_fields=expected_fields)
6163def _share_type_get_by_name(context, name):
6164 result = _share_type_get_query(context).filter_by(name=name).first()
6166 if not result:
6167 raise exception.ShareTypeNotFoundByName(share_type_name=name)
6169 return _dict_with_specs(result)
6172@require_context
6173@context_manager.reader
6174def share_type_get_by_name(context, name):
6175 """Return a dict describing specific share_type."""
6176 return _share_type_get_by_name(context, name)
6179@require_context
6180@context_manager.reader
6181def share_type_get_by_name_or_id(context, name_or_id):
6182 """Return a dict describing specific share_type using its name or ID.
6184 :returns: ShareType object or None if not found
6185 """
6186 try:
6187 return _share_type_get(context, name_or_id)
6188 except exception.ShareTypeNotFound:
6189 try:
6190 return _share_type_get_by_name(context, name_or_id)
6191 except exception.ShareTypeNotFoundByName:
6192 return None
6195@require_admin_context
6196@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
6197@context_manager.writer
6198def share_type_destroy(context, id):
6199 _share_type_get(context, id)
6200 shares_count = model_query(
6201 context,
6202 models.ShareInstance,
6203 read_deleted="no",
6204 ).filter_by(share_type_id=id).count()
6205 share_group_types_count = model_query(
6206 context,
6207 models.ShareGroupTypeShareTypeMapping,
6208 read_deleted="no",
6209 ).filter_by(share_type_id=id).count()
6210 if shares_count or share_group_types_count:
6211 msg = ("Deletion of share type %(stype)s failed; it in use by "
6212 "%(shares)d shares and %(gtypes)d share group types")
6213 msg_args = {'stype': id,
6214 'shares': shares_count,
6215 'gtypes': share_group_types_count}
6216 LOG.error(msg, msg_args)
6217 raise exception.ShareTypeInUse(share_type_id=id)
6219 model_query(
6220 context, models.ShareTypeExtraSpecs,
6221 ).filter_by(
6222 share_type_id=id
6223 ).soft_delete()
6224 model_query(
6225 context, models.ShareTypeProjects,
6226 ).filter_by(
6227 share_type_id=id,
6228 ).soft_delete()
6229 model_query(
6230 context, models.ShareTypes,
6231 ).filter_by(
6232 id=id
6233 ).soft_delete()
6235 # NOTE(stephenfin): commit changes before we do anything with quotas
6237 context.session.commit()
6238 context.session.begin()
6240 # Destroy any quotas, usages and reservations for the share type:
6241 _quota_destroy_all_by_share_type(context, id)
6244def _share_type_access_query(context):
6245 return model_query(context, models.ShareTypeProjects, read_deleted="no")
6248@require_admin_context
6249@context_manager.reader
6250def share_type_access_get_all(context, type_id):
6251 share_type_id = _share_type_get_id_from_share_type(context, type_id)
6252 return _share_type_access_query(
6253 context,
6254 ).filter_by(share_type_id=share_type_id).all()
6257@require_admin_context
6258@context_manager.writer
6259def share_type_access_add(context, type_id, project_id):
6260 """Add given tenant to the share type access list."""
6261 share_type_id = _share_type_get_id_from_share_type(context, type_id)
6263 access_ref = models.ShareTypeProjects()
6264 access_ref.update(
6265 {"share_type_id": share_type_id, "project_id": project_id},
6266 )
6268 try:
6269 access_ref.save(session=context.session)
6270 except db_exception.DBDuplicateEntry:
6271 raise exception.ShareTypeAccessExists(
6272 share_type_id=type_id, project_id=project_id,
6273 )
6274 return access_ref
6277@require_admin_context
6278@context_manager.writer
6279def share_type_access_remove(context, type_id, project_id):
6280 """Remove given tenant from the share type access list."""
6281 share_type_id = _share_type_get_id_from_share_type(context, type_id)
6283 count = _share_type_access_query(
6284 context,
6285 ).filter_by(
6286 share_type_id=share_type_id,
6287 ).filter_by(
6288 project_id=project_id,
6289 ).soft_delete(synchronize_session=False)
6291 if count == 0: 6291 ↛ 6292line 6291 didn't jump to line 6292 because the condition on line 6291 was never true
6292 raise exception.ShareTypeAccessNotFound(
6293 share_type_id=type_id, project_id=project_id,
6294 )
6296####################
6299def _share_type_extra_specs_query(context, share_type_id):
6300 return model_query(
6301 context, models.ShareTypeExtraSpecs, read_deleted="no",
6302 ).filter_by(
6303 share_type_id=share_type_id,
6304 ).options(orm.joinedload(models.ShareTypeExtraSpecs.share_type))
6307@require_context
6308@context_manager.reader
6309def share_type_extra_specs_get(context, share_type_id):
6310 rows = _share_type_extra_specs_query(context, share_type_id).all()
6311 result = {}
6312 for row in rows: 6312 ↛ 6313line 6312 didn't jump to line 6313 because the loop on line 6312 never started
6313 result[row['key']] = row['value']
6315 return result
6318@require_context
6319@context_manager.writer
6320def share_type_extra_specs_delete(context, share_type_id, key):
6321 _share_type_extra_specs_get_item(context, share_type_id, key)
6322 _share_type_extra_specs_query(
6323 context, share_type_id,
6324 ).filter_by(key=key).soft_delete()
6327def _share_type_extra_specs_get_item(context, share_type_id, key):
6328 result = _share_type_extra_specs_query(
6329 context, share_type_id,
6330 ).filter_by(
6331 key=key,
6332 ).options(
6333 orm.joinedload(models.ShareTypeExtraSpecs.share_type),
6334 ).first()
6336 if not result:
6337 raise exception.ShareTypeExtraSpecsNotFound(
6338 extra_specs_key=key,
6339 share_type_id=share_type_id,
6340 )
6342 return result
6345@require_context
6346@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
6347@context_manager.writer
6348def share_type_extra_specs_update_or_create(context, share_type_id, specs):
6349 spec_ref = None
6350 for key, value in specs.items():
6351 try:
6352 spec_ref = _share_type_extra_specs_get_item(
6353 context, share_type_id, key,
6354 )
6355 except exception.ShareTypeExtraSpecsNotFound:
6356 spec_ref = models.ShareTypeExtraSpecs()
6357 spec_ref.update(
6358 {
6359 "key": key,
6360 "value": value,
6361 "share_type_id": share_type_id,
6362 "deleted": 0,
6363 }
6364 )
6365 spec_ref.save(session=context.session)
6367 return specs
6369####################
6372@context_manager.writer
6373def ensure_availability_zone_exists(context, values, *, strict=True):
6374 az_name = values.pop('availability_zone', None)
6376 if not az_name:
6377 if strict:
6378 msg = _("Values dict should have 'availability_zone' field.")
6379 raise ValueError(msg)
6380 return
6382 if uuidutils.is_uuid_like(az_name): 6382 ↛ 6383line 6382 didn't jump to line 6383 because the condition on line 6382 was never true
6383 az_ref = _availability_zone_get(context, az_name)
6384 else:
6385 az_ref = _availability_zone_create_if_not_exist(context, az_name)
6387 values.update({'availability_zone_id': az_ref['id']})
6390@require_context
6391@context_manager.reader
6392def availability_zone_get(context, id_or_name):
6393 return _availability_zone_get(context, id_or_name)
6396@require_context
6397def _availability_zone_get(context, id_or_name):
6398 query = model_query(context, models.AvailabilityZone)
6400 if uuidutils.is_uuid_like(id_or_name):
6401 query = query.filter_by(id=id_or_name)
6402 else:
6403 query = query.filter_by(name=id_or_name)
6405 result = query.first()
6407 if not result:
6408 raise exception.AvailabilityZoneNotFound(id=id_or_name)
6410 return result
6413@require_context
6414@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
6415def _availability_zone_create_if_not_exist(context, name):
6416 try:
6417 return _availability_zone_get(context, name)
6418 except exception.AvailabilityZoneNotFound:
6419 az = models.AvailabilityZone()
6420 az.update({'id': uuidutils.generate_uuid(), 'name': name})
6421 az.save(context.session)
6422 return az
6425@require_context
6426@context_manager.reader
6427def availability_zone_get_all(context):
6428 enabled_services = model_query(
6429 context, models.Service,
6430 models.Service.availability_zone_id,
6431 read_deleted="no"
6432 ).filter_by(disabled=False).distinct()
6434 return model_query(
6435 context, models.AvailabilityZone, read_deleted="no",
6436 ).filter(
6437 models.AvailabilityZone.id.in_(enabled_services)
6438 ).all()
6440####################
6443@require_admin_context
6444@context_manager.writer
6445def purge_deleted_records(context, age_in_days):
6446 """Purge soft-deleted records older than(and equal) age from tables."""
6448 if age_in_days < 0:
6449 msg = _('Must supply a non-negative value for "age_in_days".')
6450 LOG.error(msg)
6451 raise exception.InvalidParameterValue(msg)
6453 metadata = MetaData()
6454 metadata.reflect(get_engine())
6455 tables = metadata.sorted_tables
6456 if not tables: 6456 ↛ 6457line 6456 didn't jump to line 6457 because the condition on line 6456 was never true
6457 msg = 'No tables found, check database connection'
6458 raise exception.InvalidResults(msg)
6460 deleted_age = timeutils.utcnow() - datetime.timedelta(days=age_in_days)
6462 # Deleting rows in share_network_security_service_association
6463 # related to deleted network or security service
6464 sec_assoc_to_delete = context.session.query(
6465 models.ShareNetworkSecurityServiceAssociation).join(
6466 models.ShareNetwork).join(models.SecurityService).filter(
6467 or_(models.ShareNetwork.deleted_at <= deleted_age,
6468 models.SecurityService.deleted_at <= deleted_age)).all()
6470 for assoc in sec_assoc_to_delete:
6471 with context.session.begin_nested():
6472 context.session.delete(assoc)
6474 for table in reversed(tables):
6475 if 'deleted' not in table.columns.keys():
6476 continue
6478 try:
6479 mds = [m for m in models.__dict__.values() if
6480 (hasattr(m, '__tablename__') and
6481 m.__tablename__ == str(table))]
6482 if len(mds) > 0: 6482 ↛ 6474line 6482 didn't jump to line 6474 because the condition on line 6482 was always true
6483 # collect all soft-deleted records
6484 with context.session.begin_nested():
6485 model = mds[0]
6486 s_deleted_records = context.session.query(
6487 model,
6488 ).filter(model.deleted_at <= deleted_age)
6489 deleted_count = 0
6490 # delete records one by one,
6491 # skip the records which has FK constraints
6492 for record in s_deleted_records:
6493 try:
6494 with context.session.begin_nested():
6495 context.session.delete(record)
6496 deleted_count += 1
6497 except db_exc.DBError:
6498 LOG.warning(
6499 ("Deleting soft-deleted resource %s "
6500 "failed, skipping."), record)
6501 if deleted_count != 0:
6502 LOG.info("Deleted %(count)s records in "
6503 "table %(table)s.",
6504 {'count': deleted_count, 'table': table})
6505 except db_exc.DBError:
6506 LOG.warning("Querying table %s's soft-deleted records "
6507 "failed, skipping.", table)
6510####################
6513def _share_group_get(context, share_group_id):
6514 result = model_query(
6515 context, models.ShareGroup, project_only=True, read_deleted='no',
6516 ).filter_by(
6517 id=share_group_id,
6518 ).options(orm.joinedload(models.ShareGroup.share_types)).first()
6520 if not result:
6521 raise exception.ShareGroupNotFound(share_group_id=share_group_id)
6523 return result
6526@require_context
6527@context_manager.reader
6528def share_group_get(context, share_group_id):
6529 return _share_group_get(context, share_group_id)
6532def _share_group_get_all(context, project_id=None, share_server_id=None,
6533 host=None, detailed=True, filters=None,
6534 sort_key=None, sort_dir=None):
6535 sort_key = sort_key or 'created_at'
6536 sort_dir = sort_dir or 'desc'
6538 query = model_query(
6539 context, models.ShareGroup, read_deleted='no')
6541 # Apply filters
6542 if not filters:
6543 filters = {}
6544 no_key = 'key_is_absent'
6545 for k, v in filters.items():
6546 temp_k = k.rstrip('~') if k in constants.LIKE_FILTER else k
6547 filter_attr = getattr(models.ShareGroup, temp_k, no_key)
6549 if filter_attr == no_key: 6549 ↛ 6550line 6549 didn't jump to line 6550 because the condition on line 6549 was never true
6550 msg = _("Share groups cannot be filtered using '%s' key.")
6551 raise exception.InvalidInput(reason=msg % k)
6553 if k in constants.LIKE_FILTER:
6554 query = query.filter(filter_attr.op('LIKE')(u'%' + v + u'%'))
6555 else:
6556 query = query.filter(filter_attr == v)
6558 if project_id:
6559 query = query.filter(
6560 models.ShareGroup.project_id == project_id)
6561 if host:
6562 query = query.filter(
6563 models.ShareGroup.host == host)
6564 if share_server_id:
6565 query = query.filter(
6566 models.ShareGroup.share_server_id == share_server_id)
6568 try:
6569 query = apply_sorting(models.ShareGroup, query, sort_key, sort_dir)
6570 except AttributeError:
6571 msg = _("Wrong sorting key provided - '%s'.") % sort_key
6572 raise exception.InvalidInput(reason=msg)
6574 if detailed:
6575 return query.options(
6576 orm.joinedload(models.ShareGroup.share_types),
6577 ).all()
6579 query = query.with_entities(
6580 models.ShareGroup.id, models.ShareGroup.name)
6581 values = []
6582 for sg_id, sg_name in query.all():
6583 values.append({"id": sg_id, "name": sg_name})
6584 return values
6587@require_admin_context
6588@context_manager.reader
6589def share_group_get_all(context, detailed=True, filters=None, sort_key=None,
6590 sort_dir=None):
6591 return _share_group_get_all(
6592 context, detailed=detailed, filters=filters,
6593 sort_key=sort_key, sort_dir=sort_dir)
6596@require_admin_context
6597@context_manager.reader
6598def share_group_get_all_by_host(context, host, detailed=True):
6599 return _share_group_get_all(context, host=host, detailed=detailed)
6602@require_context
6603@context_manager.reader
6604def share_group_get_all_by_project(context, project_id, detailed=True,
6605 filters=None, sort_key=None, sort_dir=None):
6606 authorize_project_context(context, project_id)
6607 return _share_group_get_all(
6608 context, project_id=project_id, detailed=detailed, filters=filters,
6609 sort_key=sort_key, sort_dir=sort_dir)
6612@require_context
6613@context_manager.reader
6614def share_group_get_all_by_share_server(context, share_server_id, filters=None,
6615 sort_key=None, sort_dir=None):
6616 return _share_group_get_all(
6617 context, share_server_id=share_server_id, filters=filters,
6618 sort_key=sort_key, sort_dir=sort_dir)
6621@require_context
6622@context_manager.writer
6623def share_group_create(context, values):
6624 share_group = models.ShareGroup()
6625 if not values.get('id'): 6625 ↛ 6628line 6625 didn't jump to line 6628 because the condition on line 6625 was always true
6626 values['id'] = uuidutils.generate_uuid()
6628 mappings = []
6629 for item in values.get('share_types') or []:
6630 mapping = models.ShareGroupShareTypeMapping()
6631 mapping['id'] = uuidutils.generate_uuid()
6632 mapping['share_type_id'] = item
6633 mapping['share_group_id'] = values['id']
6634 mappings.append(mapping)
6636 values['share_types'] = mappings
6638 share_group.update(values)
6639 context.session.add(share_group)
6641 return _share_group_get(context, values['id'])
6644@require_context
6645@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
6646@context_manager.writer
6647def share_group_update(context, share_group_id, values):
6648 share_group_ref = _share_group_get(
6649 context, share_group_id)
6650 share_group_ref.update(values)
6651 share_group_ref.save(session=context.session)
6652 return share_group_ref
6655@require_admin_context
6656@context_manager.writer
6657def share_group_destroy(context, share_group_id):
6658 share_group_ref = _share_group_get(context, share_group_id)
6659 share_group_ref.soft_delete(context.session)
6660 context.session.query(models.ShareGroupShareTypeMapping).filter_by(
6661 share_group_id=share_group_ref['id']).soft_delete()
6664@require_context
6665@context_manager.reader
6666def count_shares_in_share_group(context, share_group_id):
6667 return (model_query(context, models.Share,
6668 project_only=True, read_deleted="no").
6669 filter_by(share_group_id=share_group_id).
6670 count())
6673@require_context
6674@context_manager.reader
6675def get_all_shares_by_share_group(context, share_group_id):
6676 return (model_query(
6677 context, models.Share,
6678 project_only=True, read_deleted="no").
6679 filter_by(share_group_id=share_group_id).
6680 all())
6683@require_context
6684def _count_share_groups(context, project_id, user_id=None, share_type_id=None):
6685 query = model_query(
6686 context, models.ShareGroup,
6687 func.count(models.ShareGroup.id),
6688 read_deleted="no",
6689 ).filter_by(project_id=project_id)
6690 if share_type_id:
6691 query = query.join(
6692 # models.ShareGroupShareTypeMapping,
6693 models.ShareGroup.share_types,
6694 ).filter_by(share_type_id=share_type_id)
6695 elif user_id is not None:
6696 query = query.filter_by(user_id=user_id)
6697 return query.first()[0]
6700@require_context
6701def _count_share_group_snapshots(
6702 context, project_id, user_id=None, share_type_id=None,
6703):
6704 query = model_query(
6705 context, models.ShareGroupSnapshot,
6706 func.count(models.ShareGroupSnapshot.id),
6707 read_deleted="no",
6708 ).filter_by(project_id=project_id)
6709 if share_type_id:
6710 query = query.join(
6711 models.ShareGroupSnapshot.share_group,
6712 ).join(
6713 # models.ShareGroupShareTypeMapping,
6714 models.ShareGroup.share_types,
6715 ).filter_by(share_type_id=share_type_id)
6716 elif user_id is not None:
6717 query = query.filter_by(user_id=user_id)
6718 return query.first()[0]
6721@require_context
6722def _share_replica_data_get_for_project(
6723 context, project_id, user_id=None, share_type_id=None,
6724):
6725 query = model_query(
6726 context, models.ShareInstance,
6727 func.count(models.ShareInstance.id),
6728 func.sum(models.Share.size),
6729 read_deleted="no",
6730 ).join(
6731 models.Share,
6732 models.ShareInstance.share_id == models.Share.id
6733 ).filter(
6734 models.Share.project_id == project_id
6735 ).filter(
6736 models.ShareInstance.replica_state.isnot(None)
6737 )
6739 if share_type_id:
6740 query = query.filter(
6741 models.ShareInstance.share_type_id == share_type_id)
6742 elif user_id:
6743 query = query.filter(models.Share.user_id == user_id)
6745 result = query.first()
6746 return result[0] or 0, result[1] or 0
6749@require_context
6750def _count_encryption_keys_for_project(
6751 context, project_id, user_id=None,
6752):
6753 return encryption_keys_get_count(
6754 context, filters={'project_id': project_id}
6755 )
6758@require_context
6759@context_manager.reader
6760def count_share_group_snapshots_in_share_group(context, share_group_id):
6761 return model_query(
6762 context, models.ShareGroupSnapshot,
6763 project_only=True, read_deleted="no",
6764 ).filter_by(
6765 share_group_id=share_group_id,
6766 ).count()
6769@require_context
6770@context_manager.reader
6771def count_share_groups_in_share_network(context, share_network_id):
6772 return (model_query(
6773 context, models.ShareGroup,
6774 project_only=True, read_deleted="no").
6775 filter_by(share_network_id=share_network_id).
6776 count())
6779@require_context
6780@context_manager.reader
6781def count_share_group_snapshot_members_in_share(
6782 context, share_id, include_deferred_deleting=True
6783):
6784 query = model_query(
6785 context, models.ShareSnapshotInstance,
6786 project_only=True, read_deleted="no",
6787 ).join(
6788 models.ShareInstance,
6789 models.ShareInstance.id == (
6790 models.ShareSnapshotInstance.share_instance_id),
6791 )
6793 if include_deferred_deleting: 6793 ↛ 6799line 6793 didn't jump to line 6799 because the condition on line 6793 was always true
6794 # consider deferred deleting states in query
6795 return query.filter(
6796 models.ShareInstance.share_id == share_id,
6797 ).count()
6799 deferred_delete_states = [
6800 constants.STATUS_DEFERRED_DELETING,
6801 constants.STATUS_ERROR_DEFERRED_DELETING,
6802 ]
6803 return query.filter(
6804 models.ShareInstance.share_id == share_id,
6805 and_(models.ShareSnapshotInstance.status.not_in(
6806 deferred_delete_states))
6807 ).count()
6810####################
6813@require_context
6814def _share_group_snapshot_get(context, share_group_snapshot_id):
6815 result = model_query(
6816 context,
6817 models.ShareGroupSnapshot,
6818 project_only=True,
6819 read_deleted='no',
6820 ).options(
6821 orm.joinedload(models.ShareGroupSnapshot.share_group),
6822 orm.joinedload(models.ShareGroupSnapshot.share_group_snapshot_members),
6823 ).filter_by(
6824 id=share_group_snapshot_id,
6825 ).first()
6827 if not result:
6828 raise exception.ShareGroupSnapshotNotFound(
6829 share_group_snapshot_id=share_group_snapshot_id)
6831 return result
6834def _share_group_snapshot_get_all(
6835 context,
6836 project_id=None,
6837 detailed=True,
6838 filters=None,
6839 sort_key=None,
6840 sort_dir=None,
6841):
6842 if not sort_key: 6842 ↛ 6844line 6842 didn't jump to line 6844 because the condition on line 6842 was always true
6843 sort_key = 'created_at'
6844 if not sort_dir: 6844 ↛ 6847line 6844 didn't jump to line 6847 because the condition on line 6844 was always true
6845 sort_dir = 'desc'
6847 query = model_query(context, models.ShareGroupSnapshot, read_deleted='no')
6849 # Apply filters
6850 if not filters: 6850 ↛ 6852line 6850 didn't jump to line 6852 because the condition on line 6850 was always true
6851 filters = {}
6852 no_key = 'key_is_absent'
6853 for k, v in filters.items(): 6853 ↛ 6854line 6853 didn't jump to line 6854 because the loop on line 6853 never started
6854 filter_attr = getattr(models.ShareGroupSnapshot, k, no_key)
6855 if filter_attr == no_key:
6856 msg = _("Share group snapshots cannot be filtered using '%s' key.")
6857 raise exception.InvalidInput(reason=msg % k)
6858 query = query.filter(filter_attr == v)
6860 if project_id:
6861 query = query.filter(
6862 models.ShareGroupSnapshot.project_id == project_id)
6864 try:
6865 query = apply_sorting(
6866 models.ShareGroupSnapshot, query, sort_key, sort_dir)
6867 except AttributeError:
6868 msg = _("Wrong sorting key provided - '%s'.") % sort_key
6869 raise exception.InvalidInput(reason=msg)
6871 if detailed:
6872 return query.options(
6873 orm.joinedload(models.ShareGroupSnapshot.share_group),
6874 orm.joinedload(
6875 models.ShareGroupSnapshot.share_group_snapshot_members
6876 ),
6877 ).all()
6879 query = query.with_entities(models.ShareGroupSnapshot.id,
6880 models.ShareGroupSnapshot.name)
6881 values = []
6882 for sgs_id, sgs_name in query.all():
6883 values.append({"id": sgs_id, "name": sgs_name})
6884 return values
6887@require_context
6888@context_manager.reader
6889def share_group_snapshot_get(context, share_group_snapshot_id):
6890 return _share_group_snapshot_get(context, share_group_snapshot_id)
6893@require_admin_context
6894@context_manager.reader
6895def share_group_snapshot_get_all(
6896 context, detailed=True, filters=None, sort_key=None, sort_dir=None):
6897 return _share_group_snapshot_get_all(
6898 context, filters=filters, detailed=detailed,
6899 sort_key=sort_key, sort_dir=sort_dir)
6902@require_context
6903@context_manager.reader
6904def share_group_snapshot_get_all_by_project(
6905 context, project_id, detailed=True, filters=None,
6906 sort_key=None, sort_dir=None):
6907 authorize_project_context(context, project_id)
6908 return _share_group_snapshot_get_all(
6909 context, project_id=project_id, filters=filters, detailed=detailed,
6910 sort_key=sort_key, sort_dir=sort_dir,
6911 )
6914@require_context
6915@context_manager.writer
6916def share_group_snapshot_create(context, values):
6917 share_group_snapshot = models.ShareGroupSnapshot()
6918 if not values.get('id'): 6918 ↛ 6921line 6918 didn't jump to line 6921 because the condition on line 6918 was always true
6919 values['id'] = uuidutils.generate_uuid()
6921 share_group_snapshot.update(values)
6922 context.session.add(share_group_snapshot)
6924 return _share_group_snapshot_get(context, values['id'])
6927@require_context
6928@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
6929@context_manager.writer
6930def share_group_snapshot_update(context, share_group_snapshot_id, values):
6931 share_group_ref = _share_group_snapshot_get(
6932 context, share_group_snapshot_id,
6933 )
6934 share_group_ref.update(values)
6935 share_group_ref.save(session=context.session)
6936 return share_group_ref
6939@require_admin_context
6940@context_manager.writer
6941def share_group_snapshot_destroy(context, share_group_snapshot_id):
6942 share_group_snap_ref = _share_group_snapshot_get(
6943 context,
6944 share_group_snapshot_id,
6945 )
6946 share_group_snap_ref.soft_delete(context.session)
6947 context.session.query(
6948 models.ShareSnapshotInstance
6949 ).filter_by(
6950 share_group_snapshot_id=share_group_snapshot_id
6951 ).soft_delete()
6954####################
6957@require_context
6958@context_manager.reader
6959def share_group_snapshot_members_get_all(context, share_group_snapshot_id):
6960 query = model_query(
6961 context,
6962 models.ShareSnapshotInstance,
6963 read_deleted='no',
6964 ).filter_by(share_group_snapshot_id=share_group_snapshot_id)
6965 return query.all()
6968@require_context
6969@context_manager.reader
6970def share_group_snapshot_member_get(context, member_id):
6971 return _share_group_snapshot_member_get(context, member_id)
6974def _share_group_snapshot_member_get(context, member_id):
6975 result = model_query(
6976 context,
6977 models.ShareSnapshotInstance,
6978 project_only=True,
6979 read_deleted='no',
6980 ).filter_by(id=member_id).first()
6981 if not result:
6982 raise exception.ShareGroupSnapshotMemberNotFound(member_id=member_id)
6983 return result
6986@require_context
6987@context_manager.writer
6988def share_group_snapshot_member_create(context, values):
6989 if not values.get('id'): 6989 ↛ 6992line 6989 didn't jump to line 6992 because the condition on line 6989 was always true
6990 values['id'] = uuidutils.generate_uuid()
6992 _change_size_to_instance_size(values)
6994 member = models.ShareSnapshotInstance()
6995 member.update(values)
6996 context.session.add(member)
6998 return _share_group_snapshot_member_get(context, values['id'])
7001@require_context
7002@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
7003@context_manager.writer
7004def share_group_snapshot_member_update(context, member_id, values):
7005 _change_size_to_instance_size(values)
7007 member = _share_group_snapshot_member_get(context, member_id)
7008 member.update(values)
7009 context.session.add(member)
7011 return _share_group_snapshot_member_get(context, member_id)
7014####################
7017@require_admin_context
7018@context_manager.writer
7019def share_group_type_create(context, values, projects=None):
7020 """Create a new share group type.
7022 In order to pass in group specs, the values dict should contain a
7023 'group_specs' key/value pair:
7024 {'group_specs' : {'k1': 'v1', 'k2': 'v2', ...}}
7025 """
7026 values = ensure_model_dict_has_id(values)
7028 projects = projects or []
7030 try:
7031 values['group_specs'] = _metadata_refs(
7032 values.get('group_specs'), models.ShareGroupTypeSpecs)
7033 mappings = []
7034 for item in values.get('share_types', []):
7035 share_type = share_type_get_by_name_or_id(context, item)
7036 if not share_type:
7037 raise exception.ShareTypeDoesNotExist(share_type=item)
7038 mapping = models.ShareGroupTypeShareTypeMapping()
7039 mapping['id'] = uuidutils.generate_uuid()
7040 mapping['share_type_id'] = share_type['id']
7041 mapping['share_group_type_id'] = values['id']
7042 mappings.append(mapping)
7044 values['share_types'] = mappings
7045 share_group_type_ref = models.ShareGroupTypes()
7046 share_group_type_ref.update(values)
7047 share_group_type_ref.save(session=context.session)
7048 except db_exception.DBDuplicateEntry:
7049 raise exception.ShareGroupTypeExists(type_id=values['name'])
7050 except exception.ShareTypeDoesNotExist:
7051 raise
7052 except Exception as e:
7053 raise db_exception.DBError(e)
7055 for project in set(projects):
7056 access_ref = models.ShareGroupTypeProjects()
7057 access_ref.update({"share_group_type_id": share_group_type_ref.id,
7058 "project_id": project})
7059 access_ref.save(session=context.session)
7061 return share_group_type_ref
7064def _share_group_type_get_query(
7065 context,
7066 read_deleted=None,
7067 expected_fields=None,
7068):
7069 expected_fields = expected_fields or []
7070 query = model_query(
7071 context,
7072 models.ShareGroupTypes,
7073 read_deleted=read_deleted
7074 ).options(
7075 orm.joinedload(models.ShareGroupTypes.group_specs),
7076 orm.joinedload(models.ShareGroupTypes.share_types),
7077 )
7079 if 'projects' in expected_fields: 7079 ↛ 7080line 7079 didn't jump to line 7080 because the condition on line 7079 was never true
7080 query = query.options(orm.joinedload(models.ShareGroupTypes.projects))
7082 if not context.is_admin: 7082 ↛ 7083line 7082 didn't jump to line 7083 because the condition on line 7082 was never true
7083 the_filter = [models.ShareGroupTypes.is_public == true()]
7084 projects_attr = getattr(models.ShareGroupTypes, 'projects')
7085 the_filter.extend([
7086 projects_attr.any(project_id=context.project_id)
7087 ])
7088 query = query.filter(or_(*the_filter))
7090 return query
7093@require_context
7094@context_manager.reader
7095def share_group_type_get_all(context, inactive=False, filters=None):
7096 """Returns a dict describing all share group types with name as key."""
7097 filters = filters or {}
7098 read_deleted = "yes" if inactive else "no"
7099 query = _share_group_type_get_query(context, read_deleted=read_deleted)
7101 if 'is_public' in filters and filters['is_public'] is not None:
7102 the_filter = [models.ShareGroupTypes.is_public == filters['is_public']]
7103 if filters['is_public'] and context.project_id is not None:
7104 projects_attr = getattr(models. ShareGroupTypes, 'projects')
7105 the_filter.extend([
7106 projects_attr.any(
7107 project_id=context.project_id, deleted=0)
7108 ])
7109 if len(the_filter) > 1:
7110 query = query.filter(or_(*the_filter))
7111 else:
7112 query = query.filter(the_filter[0])
7114 rows = query.order_by("name").all()
7116 result = {}
7117 for row in rows:
7118 result[row['name']] = _dict_with_specs(row, 'group_specs')
7120 return result
7123def _share_group_type_get_id_from_share_group_type_query(context, type_id):
7124 return model_query(
7125 context,
7126 models.ShareGroupTypes,
7127 read_deleted="no",
7128 ).filter_by(id=type_id)
7131def _share_group_type_get_id_from_share_group_type(context, type_id):
7132 result = _share_group_type_get_id_from_share_group_type_query(
7133 context,
7134 type_id,
7135 ).first()
7136 if not result:
7137 raise exception.ShareGroupTypeNotFound(type_id=type_id)
7138 return result['id']
7141@require_context
7142def _share_group_type_get(
7143 context,
7144 type_id,
7145 inactive=False,
7146 expected_fields=None,
7147):
7148 expected_fields = expected_fields or []
7149 read_deleted = "yes" if inactive else "no"
7150 result = _share_group_type_get_query(
7151 context,
7152 read_deleted,
7153 expected_fields,
7154 ).filter_by(id=type_id).first()
7156 if not result:
7157 raise exception.ShareGroupTypeNotFound(type_id=type_id)
7159 share_group_type = _dict_with_specs(result, 'group_specs')
7161 if 'projects' in expected_fields: 7161 ↛ 7162line 7161 didn't jump to line 7162 because the condition on line 7161 was never true
7162 share_group_type['projects'] = [
7163 p['project_id'] for p in result['projects']]
7165 return share_group_type
7168@require_context
7169@context_manager.reader
7170def share_group_type_get(context, type_id, inactive=False,
7171 expected_fields=None):
7172 """Return a dict describing specific share group type."""
7173 return _share_group_type_get(
7174 context,
7175 type_id,
7176 inactive=inactive,
7177 expected_fields=expected_fields,
7178 )
7181@require_context
7182def _share_group_type_get_by_name(context, name):
7183 result = model_query(
7184 context,
7185 models.ShareGroupTypes,
7186 ).options(
7187 orm.joinedload(models.ShareGroupTypes.group_specs),
7188 orm.joinedload(models.ShareGroupTypes.share_types),
7189 ).filter_by(
7190 name=name,
7191 ).first()
7192 if not result:
7193 raise exception.ShareGroupTypeNotFoundByName(type_name=name)
7194 return _dict_with_specs(result, 'group_specs')
7197@require_context
7198@context_manager.reader
7199def share_group_type_get_by_name(context, name):
7200 """Return a dict describing specific share group type."""
7201 return _share_group_type_get_by_name(context, name)
7204@require_admin_context
7205@context_manager.writer
7206def share_group_type_destroy(context, type_id):
7207 _share_group_type_get(context, type_id)
7208 results = model_query(
7209 context,
7210 models.ShareGroup,
7211 read_deleted="no",
7212 ).filter_by(
7213 share_group_type_id=type_id,
7214 ).count()
7215 if results:
7216 LOG.error('Share group type %s deletion failed, it in use.',
7217 type_id)
7218 raise exception.ShareGroupTypeInUse(type_id=type_id)
7220 model_query(
7221 context,
7222 models.ShareGroupTypeSpecs,
7223 ).filter_by(
7224 share_group_type_id=type_id,
7225 ).soft_delete()
7227 model_query(
7228 context,
7229 models.ShareGroupTypeShareTypeMapping,
7230 ).filter_by(
7231 share_group_type_id=type_id,
7232 ).soft_delete()
7234 model_query(
7235 context,
7236 models.ShareGroupTypeProjects,
7237 ).filter_by(
7238 share_group_type_id=type_id,
7239 ).soft_delete()
7241 model_query(
7242 context,
7243 models.ShareGroupTypes,
7244 ).filter_by(
7245 id=type_id,
7246 ).soft_delete()
7249###############################
7252def _share_group_type_access_query(context):
7253 return model_query(
7254 context,
7255 models.ShareGroupTypeProjects,
7256 read_deleted="no",
7257 )
7260@require_admin_context
7261@context_manager.reader
7262def share_group_type_access_get_all(context, type_id):
7263 share_group_type_id = _share_group_type_get_id_from_share_group_type(
7264 context, type_id)
7265 return _share_group_type_access_query(context).filter_by(
7266 share_group_type_id=share_group_type_id,
7267 ).all()
7270@require_admin_context
7271@context_manager.writer
7272def share_group_type_access_add(context, type_id, project_id):
7273 """Add given tenant to the share group type access list."""
7274 share_group_type_id = _share_group_type_get_id_from_share_group_type(
7275 context, type_id)
7276 access_ref = models.ShareGroupTypeProjects()
7277 access_ref.update({"share_group_type_id": share_group_type_id,
7278 "project_id": project_id})
7279 try:
7280 access_ref.save(session=context.session)
7281 except db_exception.DBDuplicateEntry:
7282 raise exception.ShareGroupTypeAccessExists(
7283 type_id=share_group_type_id, project_id=project_id)
7284 return access_ref
7287@require_admin_context
7288@context_manager.writer
7289def share_group_type_access_remove(context, type_id, project_id):
7290 """Remove given tenant from the share group type access list."""
7291 share_group_type_id = _share_group_type_get_id_from_share_group_type(
7292 context, type_id)
7293 count = _share_group_type_access_query(context).filter_by(
7294 share_group_type_id=share_group_type_id,
7295 ).filter_by(
7296 project_id=project_id,
7297 ).soft_delete(
7298 synchronize_session=False,
7299 )
7300 if count == 0: 7300 ↛ 7301line 7300 didn't jump to line 7301 because the condition on line 7300 was never true
7301 raise exception.ShareGroupTypeAccessNotFound(
7302 type_id=share_group_type_id, project_id=project_id)
7305###############################
7308def _share_group_type_specs_query(context, type_id):
7309 return model_query(
7310 context,
7311 models.ShareGroupTypeSpecs,
7312 read_deleted="no"
7313 ).filter_by(
7314 share_group_type_id=type_id,
7315 ).options(
7316 orm.joinedload(models.ShareGroupTypeSpecs.share_group_type),
7317 )
7320@require_context
7321@context_manager.reader
7322def share_group_type_specs_get(context, type_id):
7323 rows = _share_group_type_specs_query(context, type_id).all()
7324 result = {}
7325 for row in rows: 7325 ↛ 7326line 7325 didn't jump to line 7326 because the loop on line 7325 never started
7326 result[row['key']] = row['value']
7327 return result
7330@require_context
7331@context_manager.writer
7332def share_group_type_specs_delete(context, type_id, key):
7333 _share_group_type_specs_get_item(context, type_id, key)
7334 _share_group_type_specs_query(
7335 context,
7336 type_id,
7337 ).filter_by(
7338 key=key,
7339 ).soft_delete()
7342@require_context
7343def _share_group_type_specs_get_item(context, type_id, key):
7344 result = _share_group_type_specs_query(
7345 context,
7346 type_id,
7347 ).filter_by(
7348 key=key,
7349 ).options(
7350 orm.joinedload(models.ShareGroupTypeSpecs.share_group_type),
7351 ).first()
7353 if not result:
7354 raise exception.ShareGroupTypeSpecsNotFound(
7355 specs_key=key, type_id=type_id)
7357 return result
7360@require_context
7361@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
7362@context_manager.writer
7363def share_group_type_specs_update_or_create(context, type_id, specs):
7364 spec_ref = None
7365 for key, value in specs.items():
7366 try:
7367 spec_ref = _share_group_type_specs_get_item(
7368 context,
7369 type_id,
7370 key,
7371 )
7372 except exception.ShareGroupTypeSpecsNotFound:
7373 spec_ref = models.ShareGroupTypeSpecs()
7374 spec_ref.update({"key": key, "value": value,
7375 "share_group_type_id": type_id, "deleted": 0})
7376 spec_ref.save(session=context.session)
7378 return specs
7381###############################
7384@require_context
7385def _message_get(context, message_id):
7386 query = model_query(context,
7387 models.Message,
7388 read_deleted="no",
7389 project_only="yes")
7390 result = query.filter_by(id=message_id).first()
7391 if not result:
7392 raise exception.MessageNotFound(message_id=message_id)
7393 return result
7396@require_context
7397@context_manager.reader
7398def message_get(context, message_id):
7399 return _message_get(context, message_id)
7402@require_context
7403@context_manager.reader
7404def message_get_all(context, filters=None, limit=None, offset=None,
7405 sort_key='created_at', sort_dir='desc'):
7406 """Retrieves all messages.
7408 If no sort parameters are specified then the returned messages are
7409 sorted by the 'created_at' key in descending order.
7411 :param context: context to query under
7412 :param limit: maximum number of items to return
7413 :param offset: the number of items to skip from the marker or from the
7414 first element.
7415 :param sort_key: attributes by which results should be sorted.
7416 :param sort_dir: directions in which results should be sorted.
7417 :param filters: dictionary of filters; values that are in lists, tuples,
7418 or sets cause an 'IN' operation, while exact matching
7419 is used for other values, see exact_filter function for
7420 more information
7421 :returns: list of matching messages
7422 """
7423 messages = models.Message
7425 query = model_query(context,
7426 messages,
7427 read_deleted="no",
7428 project_only="yes")
7430 legal_filter_keys = ('request_id', 'resource_type', 'resource_id',
7431 'action_id', 'detail_id', 'message_level',
7432 'created_since', 'created_before')
7434 if not filters:
7435 filters = {}
7437 query = exact_filter(query, messages, filters, legal_filter_keys)
7439 query = utils.paginate_query(query, messages, limit,
7440 sort_key=sort_key,
7441 sort_dir=sort_dir,
7442 offset=offset)
7444 return query.all()
7447@require_context
7448@context_manager.writer
7449def message_create(context, message_values):
7450 values = copy.deepcopy(message_values)
7451 message_ref = models.Message()
7452 if not values.get('id'): 7452 ↛ 7454line 7452 didn't jump to line 7454 because the condition on line 7452 was always true
7453 values['id'] = uuidutils.generate_uuid()
7454 message_ref.update(values)
7456 context.session.add(message_ref)
7458 return _message_get(context, message_ref['id'])
7461@require_context
7462@context_manager.writer
7463def message_destroy(context, message):
7464 model_query(
7465 context, models.Message,
7466 ).filter_by(id=message.get('id')).soft_delete()
7469@require_admin_context
7470@context_manager.writer
7471def cleanup_expired_messages(context):
7472 now = timeutils.utcnow()
7473 return context.session.query(
7474 models.Message
7475 ).filter(
7476 models.Message.expires_at < now
7477 ).delete()
7480###############################
7483@require_context
7484@context_manager.reader
7485def backend_info_get(context, host):
7486 """Get hash info for given host."""
7487 result = _backend_info_query(context, host)
7488 return result
7491@require_context
7492@context_manager.writer
7493def backend_info_create(context, host, value):
7494 info_ref = models.BackendInfo()
7495 info_ref.update({"host": host, "info_hash": value})
7496 info_ref.save(context.session)
7497 return info_ref
7500@require_context
7501@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
7502@context_manager.writer
7503def backend_info_update(context, host, value=None, delete_existing=False):
7504 """Remove backend info for host name."""
7505 info_ref = _backend_info_query(context, host)
7506 if info_ref:
7507 if value:
7508 info_ref.update({"info_hash": value})
7509 elif delete_existing and info_ref['deleted'] != 1: 7509 ↛ 7514line 7509 didn't jump to line 7514 because the condition on line 7509 was always true
7510 info_ref.update({"deleted": 1, "deleted_at": timeutils.utcnow()})
7511 else:
7512 info_ref = models.BackendInfo()
7513 info_ref.update({"host": host, "info_hash": value})
7514 info_ref.save(context.session)
7515 return info_ref
7518def _backend_info_query(context, host, read_deleted=False):
7519 result = model_query(
7520 context,
7521 models.BackendInfo,
7522 read_deleted=read_deleted,
7523 ).filter_by(
7524 host=host,
7525 ).first()
7527 return result
7529###################
7532def _async_operation_data_query(
7533 context, entity_id, key=None, read_deleted=False,
7534):
7535 query = model_query(
7536 context, models.AsynchronousOperationData,
7537 read_deleted=read_deleted,
7538 ).filter_by(
7539 entity_uuid=entity_id,
7540 )
7542 if isinstance(key, list):
7543 return query.filter(models.AsynchronousOperationData.key.in_(key))
7544 elif key is not None:
7545 return query.filter_by(key=key)
7547 return query
7550@require_context
7551@context_manager.reader
7552def async_operation_data_get(context, entity_id, key=None, default=None):
7553 query = _async_operation_data_query(context, entity_id, key)
7555 if key is None or isinstance(key, list):
7556 return {item.key: item.value for item in query.all()}
7557 else:
7558 result = query.first()
7559 return result["value"] if result is not None else default
7562@require_context
7563@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
7564@context_manager.writer
7565def async_operation_data_update(
7566 context, entity_id, details, delete_existing=False,
7567):
7568 new_details = copy.deepcopy(details)
7570 # Process existing data
7571 original_data = context.session.query(
7572 models.AsynchronousOperationData).filter_by(
7573 entity_uuid=entity_id,
7574 ).all()
7576 for data_ref in original_data:
7577 in_new_details = data_ref['key'] in new_details
7579 if in_new_details:
7580 new_value = str(new_details.pop(data_ref['key']))
7581 data_ref.update({
7582 "value": new_value,
7583 "deleted": 0,
7584 "deleted_at": None
7585 })
7586 data_ref.save(session=context.session)
7587 elif delete_existing and data_ref['deleted'] != 1:
7588 data_ref.update({
7589 "deleted": 1, "deleted_at": timeutils.utcnow()
7590 })
7591 data_ref.save(session=context.session)
7593 # Add new data
7594 for key, value in new_details.items():
7595 data_ref = models.AsynchronousOperationData()
7596 data_ref.update({
7597 "entity_uuid": entity_id,
7598 "key": key,
7599 "value": str(value)
7600 })
7601 data_ref.save(session=context.session)
7603 return details
7606@require_context
7607@context_manager.writer
7608def async_operation_data_delete(context, entity_id, key=None):
7609 query = _async_operation_data_query(context, entity_id, key)
7610 query.update({"deleted": 1, "deleted_at": timeutils.utcnow()})
7613@require_context
7614@require_availability_zone_exists(strict=True)
7615@context_manager.writer
7616def share_backup_create(context, share_id, values):
7617 if not values.get('id'):
7618 values['id'] = uuidutils.generate_uuid()
7619 values.update({'share_id': share_id})
7621 share_backup_ref = models.ShareBackup()
7622 share_backup_ref.update(values)
7623 share_backup_ref.save(session=context.session)
7624 return share_backup_get(context, share_backup_ref['id'])
7627@require_context
7628@context_manager.reader
7629def share_backup_get(context, share_backup_id):
7630 result = model_query(
7631 context, models.ShareBackup, project_only=True, read_deleted="no"
7632 ).filter_by(
7633 id=share_backup_id,
7634 ).first()
7635 if result is None:
7636 raise exception.ShareBackupNotFound(backup_id=share_backup_id)
7638 return result
7641@require_context
7642@context_manager.reader
7643def share_backups_get_all(context, filters=None,
7644 limit=None, offset=None,
7645 sort_key=None, sort_dir=None):
7646 project_id = filters.pop('project_id', None) if filters else None
7647 query = _share_backups_get_with_filters(
7648 context,
7649 project_id=project_id,
7650 filters=filters, limit=limit, offset=offset,
7651 sort_key=sort_key, sort_dir=sort_dir)
7653 return query
7656def _share_backups_get_with_filters(context, project_id=None, filters=None,
7657 limit=None, offset=None,
7658 sort_key=None, sort_dir=None):
7659 """Retrieves all backups.
7661 If no sorting parameters are specified then returned backups are sorted
7662 by the 'created_at' key and desc order.
7664 :param context: context to query under
7665 :param filters: dictionary of filters
7666 :param limit: maximum number of items to return
7667 :param sort_key: attribute by which results should be sorted,default is
7668 created_at
7669 :param sort_dir: direction in which results should be sorted
7670 :returns: list of matching backups
7671 """
7672 # Init data
7673 sort_key = sort_key or 'created_at'
7674 sort_dir = sort_dir or 'desc'
7675 filters = copy.deepcopy(filters) if filters else {}
7676 query = model_query(context, models.ShareBackup)
7678 if project_id: 7678 ↛ 7679line 7678 didn't jump to line 7679 because the condition on line 7678 was never true
7679 query = query.filter_by(project_id=project_id)
7681 legal_filter_keys = ('display_name', 'display_name~',
7682 'display_description', 'display_description~',
7683 'id', 'share_id', 'host', 'topic', 'status')
7684 query = exact_filter(query, models.ShareBackup,
7685 filters, legal_filter_keys)
7687 query = apply_sorting(models.ShareBackup, query, sort_key, sort_dir)
7689 if limit is not None: 7689 ↛ 7690line 7689 didn't jump to line 7690 because the condition on line 7689 was never true
7690 query = query.limit(limit)
7692 if offset: 7692 ↛ 7693line 7692 didn't jump to line 7693 because the condition on line 7692 was never true
7693 query = query.offset(offset)
7695 return query.all()
7698@require_admin_context
7699@context_manager.reader
7700def _backup_data_get_for_project(context, project_id, user_id):
7701 query = model_query(context, models.ShareBackup,
7702 func.count(models.ShareBackup.id),
7703 func.sum(models.ShareBackup.size),
7704 read_deleted="no").\
7705 filter_by(project_id=project_id)
7707 if user_id: 7707 ↛ 7708line 7707 didn't jump to line 7708 because the condition on line 7707 was never true
7708 result = query.filter_by(user_id=user_id).first()
7709 else:
7710 result = query.first()
7712 return (result[0] or 0, result[1] or 0)
7715@require_context
7716@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
7717@require_availability_zone_exists(strict=False)
7718@context_manager.writer
7719def share_backup_update(context, backup_id, values):
7720 backup_ref = share_backup_get(context, backup_id)
7721 backup_ref.update(values)
7722 backup_ref.save(session=context.session)
7723 return backup_ref
7726@require_context
7727@context_manager.writer
7728def share_backup_delete(context, backup_id):
7729 backup_ref = share_backup_get(context, backup_id)
7730 backup_ref.soft_delete(session=context.session, update_status=True)
7732###############################
7735@require_context
7736def _resource_lock_get(context, lock_id):
7737 query = model_query(context,
7738 models.ResourceLock,
7739 read_deleted="no",
7740 project_only="yes")
7741 result = query.filter_by(id=lock_id).first()
7742 if not result:
7743 raise exception.ResourceLockNotFound(lock_id=lock_id)
7744 return result
7747@require_context
7748@context_manager.writer
7749def resource_lock_create(context, kwargs):
7750 """Create a resource lock."""
7751 values = copy.deepcopy(kwargs)
7752 lock_ref = models.ResourceLock()
7753 if not values.get('id'): 7753 ↛ 7755line 7753 didn't jump to line 7755 because the condition on line 7753 was always true
7754 values['id'] = uuidutils.generate_uuid()
7755 lock_ref.update(values)
7757 context.session.add(lock_ref)
7759 return _resource_lock_get(context, lock_ref['id'])
7762@require_context
7763@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
7764@context_manager.writer
7765def resource_lock_update(context, lock_id, kwargs):
7766 """Update a resource lock."""
7767 lock_ref = _resource_lock_get(context, lock_id)
7768 lock_ref.update(kwargs)
7769 lock_ref.save(session=context.session)
7770 return lock_ref
7773@require_context
7774@context_manager.writer
7775def resource_lock_delete(context, lock_id):
7776 """Delete a resource lock."""
7777 lock_ref = _resource_lock_get(context, lock_id)
7778 lock_ref.soft_delete(session=context.session)
7781@require_context
7782@context_manager.reader
7783def resource_lock_get(context, lock_id):
7784 """Retrieve a resource lock."""
7785 return _resource_lock_get(context, lock_id)
7788@require_context
7789@context_manager.reader
7790def resource_lock_get_all(context, filters=None, limit=None, offset=None,
7791 sort_key='created_at', sort_dir='desc',
7792 show_count=False):
7793 """Retrieve all resource locks.
7795 If no sort parameters are specified then the returned locks are
7796 sorted by the 'created_at' key in descending order.
7798 :param context: context to query under
7799 :param limit: maximum number of items to return
7800 :param offset: the number of items to skip from the marker or from the
7801 first element.
7802 :param sort_key: attributes by which results should be sorted.
7803 :param sort_dir: directions in which results should be sorted.
7804 :param filters: dictionary of filters; values that are in lists, tuples,
7805 or sets cause an 'IN' operation, while exact matching
7806 is used for other values, see exact_filter function for
7807 more information
7808 :returns: list of matching resource locks
7809 """
7810 locks = models.ResourceLock
7812 # add policy check to allow: all_projects, project_id filters
7813 filters = filters or {}
7815 query = model_query(context, locks, read_deleted="no")
7817 project_id = filters.get('project_id')
7818 all_projects = filters.get('all_projects') or filters.get('all_tenants')
7819 if project_id is None and not all_projects:
7820 filters['project_id'] = context.project_id
7822 legal_filter_keys = ('id', 'user_id', 'resource_id', 'resource_type',
7823 'lock_context', 'resource_action', 'created_since',
7824 'created_before', 'lock_reason', 'lock_reason~',
7825 'project_id')
7827 query = exact_filter(query, locks, filters, legal_filter_keys)
7829 count = query.count() if show_count else None
7831 query = utils.paginate_query(query, locks, limit,
7832 sort_key=sort_key,
7833 sort_dir=sort_dir,
7834 offset=offset)
7836 return query.all(), count
7838###############################
7841@require_context
7842@context_manager.reader
7843def encryption_keys_get_count(context, filters=None):
7844 if filters:
7845 project_id = filters.get('project_id')
7846 else:
7847 project_id = context.project_id
7849 query = model_query(
7850 context, models.EncryptionRef, read_deleted="no"
7851 ).filter_by(
7852 project_id=project_id,
7853 )
7855 return query.count()
7858@require_context
7859@context_manager.reader
7860def encryption_keys_get_all(context, filters=None):
7861 if filters: 7861 ↛ 7864line 7861 didn't jump to line 7864 because the condition on line 7861 was always true
7862 project_id = filters.get('project_id')
7863 else:
7864 project_id = context.project_id
7866 query = model_query(
7867 context, models.EncryptionRef, read_deleted="no"
7868 ).filter_by(
7869 project_id=project_id,
7870 )
7872 encryption_key_ref = filters.get('encryption_key_ref')
7873 if encryption_key_ref: 7873 ↛ 7876line 7873 didn't jump to line 7876 because the condition on line 7873 was always true
7874 query = query.filter_by(encryption_key_ref=encryption_key_ref)
7876 return query.all()