Shared filesystem management project for OpenStack.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

5364 lines
181 KiB

  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.
  18. """Implementation of SQLAlchemy backend."""
  19. import copy
  20. import datetime
  21. from functools import wraps
  22. import ipaddress
  23. import sys
  24. import warnings
  25. # NOTE(uglide): Required to override default oslo_db Query class
  26. import manila.db.sqlalchemy.query # noqa
  27. from oslo_config import cfg
  28. from oslo_db import api as oslo_db_api
  29. from oslo_db import exception as db_exc
  30. from oslo_db import exception as db_exception
  31. from oslo_db import options as db_options
  32. from oslo_db.sqlalchemy import session
  33. from oslo_db.sqlalchemy import utils as db_utils
  34. from oslo_log import log
  35. from oslo_utils import excutils
  36. from oslo_utils import timeutils
  37. from oslo_utils import uuidutils
  38. import six
  39. from sqlalchemy import MetaData
  40. from sqlalchemy import or_
  41. from sqlalchemy.orm import joinedload
  42. from sqlalchemy.orm import subqueryload
  43. from sqlalchemy.sql.expression import literal
  44. from sqlalchemy.sql.expression import true
  45. from sqlalchemy.sql import func
  46. from manila.common import constants
  47. from manila.db.sqlalchemy import models
  48. from manila.db.sqlalchemy import utils
  49. from manila import exception
  50. from manila.i18n import _
  51. from manila import quota
  52. CONF = cfg.CONF
  53. LOG = log.getLogger(__name__)
  54. QUOTAS = quota.QUOTAS
  55. _DEFAULT_QUOTA_NAME = 'default'
  56. PER_PROJECT_QUOTAS = []
  57. _FACADE = None
  58. _DEFAULT_SQL_CONNECTION = 'sqlite://'
  59. db_options.set_defaults(cfg.CONF,
  60. connection=_DEFAULT_SQL_CONNECTION)
  61. def _create_facade_lazily():
  62. global _FACADE
  63. if _FACADE is None:
  64. _FACADE = session.EngineFacade.from_config(cfg.CONF)
  65. return _FACADE
  66. def get_engine():
  67. facade = _create_facade_lazily()
  68. return facade.get_engine()
  69. def get_session(**kwargs):
  70. facade = _create_facade_lazily()
  71. return facade.get_session(**kwargs)
  72. def get_backend():
  73. """The backend is this module itself."""
  74. return sys.modules[__name__]
  75. def is_admin_context(context):
  76. """Indicates if the request context is an administrator."""
  77. if not context:
  78. warnings.warn(_('Use of empty request context is deprecated'),
  79. DeprecationWarning)
  80. raise Exception('die')
  81. return context.is_admin
  82. def is_user_context(context):
  83. """Indicates if the request context is a normal user."""
  84. if not context:
  85. return False
  86. if context.is_admin:
  87. return False
  88. if not context.user_id or not context.project_id:
  89. return False
  90. return True
  91. def authorize_project_context(context, project_id):
  92. """Ensures a request has permission to access the given project."""
  93. if is_user_context(context):
  94. if not context.project_id:
  95. raise exception.NotAuthorized()
  96. elif context.project_id != project_id:
  97. raise exception.NotAuthorized()
  98. def authorize_user_context(context, user_id):
  99. """Ensures a request has permission to access the given user."""
  100. if is_user_context(context):
  101. if not context.user_id:
  102. raise exception.NotAuthorized()
  103. elif context.user_id != user_id:
  104. raise exception.NotAuthorized()
  105. def authorize_quota_class_context(context, class_name):
  106. """Ensures a request has permission to access the given quota class."""
  107. if is_user_context(context):
  108. if not context.quota_class:
  109. raise exception.NotAuthorized()
  110. elif context.quota_class != class_name:
  111. raise exception.NotAuthorized()
  112. def require_admin_context(f):
  113. """Decorator to require admin request context.
  114. The first argument to the wrapped function must be the context.
  115. """
  116. @wraps(f)
  117. def wrapper(*args, **kwargs):
  118. if not is_admin_context(args[0]):
  119. raise exception.AdminRequired()
  120. return f(*args, **kwargs)
  121. return wrapper
  122. def require_context(f):
  123. """Decorator to require *any* user or admin context.
  124. This does no authorization for user or project access matching, see
  125. :py:func:`authorize_project_context` and
  126. :py:func:`authorize_user_context`.
  127. The first argument to the wrapped function must be the context.
  128. """
  129. @wraps(f)
  130. def wrapper(*args, **kwargs):
  131. if not is_admin_context(args[0]) and not is_user_context(args[0]):
  132. raise exception.NotAuthorized()
  133. return f(*args, **kwargs)
  134. return wrapper
  135. def require_share_exists(f):
  136. """Decorator to require the specified share to exist.
  137. Requires the wrapped function to use context and share_id as
  138. their first two arguments.
  139. """
  140. @wraps(f)
  141. def wrapper(context, share_id, *args, **kwargs):
  142. share_get(context, share_id)
  143. return f(context, share_id, *args, **kwargs)
  144. wrapper.__name__ = f.__name__
  145. return wrapper
  146. def require_share_instance_exists(f):
  147. """Decorator to require the specified share instance to exist.
  148. Requires the wrapped function to use context and share_instance_id as
  149. their first two arguments.
  150. """
  151. @wraps(f)
  152. def wrapper(context, share_instance_id, *args, **kwargs):
  153. share_instance_get(context, share_instance_id)
  154. return f(context, share_instance_id, *args, **kwargs)
  155. wrapper.__name__ = f.__name__
  156. return wrapper
  157. def apply_sorting(model, query, sort_key, sort_dir):
  158. if sort_dir.lower() not in ('desc', 'asc'):
  159. msg = _("Wrong sorting data provided: sort key is '%(sort_key)s' "
  160. "and sort direction is '%(sort_dir)s'.") % {
  161. "sort_key": sort_key, "sort_dir": sort_dir}
  162. raise exception.InvalidInput(reason=msg)
  163. sort_attr = getattr(model, sort_key)
  164. sort_method = getattr(sort_attr, sort_dir.lower())
  165. return query.order_by(sort_method())
  166. def handle_db_data_error(f):
  167. def wrapper(*args, **kwargs):
  168. try:
  169. return f(*args, **kwargs)
  170. except db_exc.DBDataError:
  171. msg = _('Error writing field to database.')
  172. LOG.exception(msg)
  173. raise exception.Invalid(msg)
  174. return wrapper
  175. def model_query(context, model, *args, **kwargs):
  176. """Query helper that accounts for context's `read_deleted` field.
  177. :param context: context to query under
  178. :param model: model to query. Must be a subclass of ModelBase.
  179. :param session: if present, the session to use
  180. :param read_deleted: if present, overrides context's read_deleted field.
  181. :param project_only: if present and context is user-type, then restrict
  182. query to match the context's project_id.
  183. """
  184. session = kwargs.get('session') or get_session()
  185. read_deleted = kwargs.get('read_deleted') or context.read_deleted
  186. project_only = kwargs.get('project_only')
  187. kwargs = dict()
  188. if project_only and not context.is_admin:
  189. kwargs['project_id'] = context.project_id
  190. if read_deleted in ('no', 'n', False):
  191. kwargs['deleted'] = False
  192. elif read_deleted in ('yes', 'y', True):
  193. kwargs['deleted'] = True
  194. return db_utils.model_query(
  195. model=model, session=session, args=args, **kwargs)
  196. def exact_filter(query, model, filters, legal_keys,
  197. created_at_key='created_at'):
  198. """Applies exact match filtering to a query.
  199. Returns the updated query. Modifies filters argument to remove
  200. filters consumed.
  201. :param query: query to apply filters to
  202. :param model: model object the query applies to, for IN-style
  203. filtering
  204. :param filters: dictionary of filters; values that are lists,
  205. tuples, sets, or frozensets cause an 'IN' test to
  206. be performed, while exact matching ('==' operator)
  207. is used for other values
  208. :param legal_keys: list of keys to apply exact filtering to
  209. """
  210. filter_dict = {}
  211. created_at_attr = getattr(model, created_at_key, None)
  212. # Walk through all the keys
  213. for key in legal_keys:
  214. # Skip ones we're not filtering on
  215. if key not in filters:
  216. continue
  217. # OK, filtering on this key; what value do we search for?
  218. value = filters.pop(key)
  219. if key == 'created_since' and created_at_attr:
  220. # This is a reserved query parameter to indicate resources created
  221. # after a particular datetime
  222. value = timeutils.normalize_time(value)
  223. query = query.filter(created_at_attr.op('>=')(value))
  224. elif key == 'created_before' and created_at_attr:
  225. # This is a reserved query parameter to indicate resources created
  226. # before a particular datetime
  227. value = timeutils.normalize_time(value)
  228. query = query.filter(created_at_attr.op('<=')(value))
  229. elif isinstance(value, (list, tuple, set, frozenset)):
  230. # Looking for values in a list; apply to query directly
  231. column_attr = getattr(model, key)
  232. query = query.filter(column_attr.in_(value))
  233. else:
  234. # OK, simple exact match; save for later
  235. filter_dict[key] = value
  236. # Apply simple exact matches
  237. if filter_dict:
  238. query = query.filter_by(**filter_dict)
  239. return query
  240. def ensure_model_dict_has_id(model_dict):
  241. if not model_dict.get('id'):
  242. model_dict['id'] = uuidutils.generate_uuid()
  243. return model_dict
  244. def _sync_shares(context, project_id, user_id, session, share_type_id=None):
  245. (shares, gigs) = share_data_get_for_project(
  246. context, project_id, user_id, share_type_id=share_type_id,
  247. session=session)
  248. return {'shares': shares}
  249. def _sync_snapshots(context, project_id, user_id, session, share_type_id=None):
  250. (snapshots, gigs) = snapshot_data_get_for_project(
  251. context, project_id, user_id, share_type_id=share_type_id,
  252. session=session)
  253. return {'snapshots': snapshots}
  254. def _sync_gigabytes(context, project_id, user_id, session, share_type_id=None):
  255. _junk, share_gigs = share_data_get_for_project(
  256. context, project_id, user_id, share_type_id=share_type_id,
  257. session=session)
  258. return {"gigabytes": share_gigs}
  259. def _sync_snapshot_gigabytes(context, project_id, user_id, session,
  260. share_type_id=None):
  261. _junk, snapshot_gigs = snapshot_data_get_for_project(
  262. context, project_id, user_id, share_type_id=share_type_id,
  263. session=session)
  264. return {"snapshot_gigabytes": snapshot_gigs}
  265. def _sync_share_networks(context, project_id, user_id, session,
  266. share_type_id=None):
  267. share_networks_count = count_share_networks(
  268. context, project_id, user_id, share_type_id=share_type_id,
  269. session=session)
  270. return {'share_networks': share_networks_count}
  271. def _sync_share_groups(context, project_id, user_id, session,
  272. share_type_id=None):
  273. share_groups_count = count_share_groups(
  274. context, project_id, user_id, share_type_id=share_type_id,
  275. session=session)
  276. return {'share_groups': share_groups_count}
  277. def _sync_share_group_snapshots(context, project_id, user_id, session,
  278. share_type_id=None):
  279. share_group_snapshots_count = count_share_group_snapshots(
  280. context, project_id, user_id, share_type_id=share_type_id,
  281. session=session)
  282. return {'share_group_snapshots': share_group_snapshots_count}
  283. QUOTA_SYNC_FUNCTIONS = {
  284. '_sync_shares': _sync_shares,
  285. '_sync_snapshots': _sync_snapshots,
  286. '_sync_gigabytes': _sync_gigabytes,
  287. '_sync_snapshot_gigabytes': _sync_snapshot_gigabytes,
  288. '_sync_share_networks': _sync_share_networks,
  289. '_sync_share_groups': _sync_share_groups,
  290. '_sync_share_group_snapshots': _sync_share_group_snapshots,
  291. }
  292. ###################
  293. @require_admin_context
  294. def service_destroy(context, service_id):
  295. session = get_session()
  296. with session.begin():
  297. service_ref = service_get(context, service_id, session=session)
  298. service_ref.soft_delete(session)
  299. @require_admin_context
  300. def service_get(context, service_id, session=None):
  301. result = (model_query(
  302. context,
  303. models.Service,
  304. session=session).
  305. filter_by(id=service_id).
  306. first())
  307. if not result:
  308. raise exception.ServiceNotFound(service_id=service_id)
  309. return result
  310. @require_admin_context
  311. def service_get_all(context, disabled=None):
  312. query = model_query(context, models.Service)
  313. if disabled is not None:
  314. query = query.filter_by(disabled=disabled)
  315. return query.all()
  316. @require_admin_context
  317. def service_get_all_by_topic(context, topic):
  318. return (model_query(
  319. context, models.Service, read_deleted="no").
  320. filter_by(disabled=False).
  321. filter_by(topic=topic).
  322. all())
  323. @require_admin_context
  324. def service_get_by_host_and_topic(context, host, topic):
  325. result = (model_query(
  326. context, models.Service, read_deleted="no").
  327. filter_by(disabled=False).
  328. filter_by(host=host).
  329. filter_by(topic=topic).
  330. first())
  331. if not result:
  332. raise exception.ServiceNotFound(service_id=host)
  333. return result
  334. @require_admin_context
  335. def _service_get_all_topic_subquery(context, session, topic, subq, label):
  336. sort_value = getattr(subq.c, label)
  337. return (model_query(context, models.Service,
  338. func.coalesce(sort_value, 0),
  339. session=session, read_deleted="no").
  340. filter_by(topic=topic).
  341. filter_by(disabled=False).
  342. outerjoin((subq, models.Service.host == subq.c.host)).
  343. order_by(sort_value).
  344. all())
  345. @require_admin_context
  346. def service_get_all_share_sorted(context):
  347. session = get_session()
  348. with session.begin():
  349. topic = CONF.share_topic
  350. label = 'share_gigabytes'
  351. subq = (model_query(context, models.Share,
  352. func.sum(models.Share.size).label(label),
  353. session=session, read_deleted="no").
  354. join(models.ShareInstance,
  355. models.ShareInstance.share_id == models.Share.id).
  356. group_by(models.ShareInstance.host).
  357. subquery())
  358. return _service_get_all_topic_subquery(context,
  359. session,
  360. topic,
  361. subq,
  362. label)
  363. @require_admin_context
  364. def service_get_by_args(context, host, binary):
  365. result = (model_query(context, models.Service).
  366. filter_by(host=host).
  367. filter_by(binary=binary).
  368. first())
  369. if not result:
  370. raise exception.HostBinaryNotFound(host=host, binary=binary)
  371. return result
  372. @require_admin_context
  373. def service_create(context, values):
  374. session = get_session()
  375. _ensure_availability_zone_exists(context, values, session)
  376. service_ref = models.Service()
  377. service_ref.update(values)
  378. if not CONF.enable_new_services:
  379. service_ref.disabled = True
  380. with session.begin():
  381. service_ref.save(session)
  382. return service_ref
  383. @require_admin_context
  384. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  385. def service_update(context, service_id, values):
  386. session = get_session()
  387. _ensure_availability_zone_exists(context, values, session, strict=False)
  388. with session.begin():
  389. service_ref = service_get(context, service_id, session=session)
  390. service_ref.update(values)
  391. service_ref.save(session=session)
  392. ###################
  393. @require_context
  394. def quota_get_all_by_project_and_user(context, project_id, user_id):
  395. authorize_project_context(context, project_id)
  396. user_quotas = model_query(
  397. context, models.ProjectUserQuota,
  398. models.ProjectUserQuota.resource,
  399. models.ProjectUserQuota.hard_limit,
  400. ).filter_by(
  401. project_id=project_id,
  402. ).filter_by(
  403. user_id=user_id,
  404. ).all()
  405. result = {'project_id': project_id, 'user_id': user_id}
  406. for u_quota in user_quotas:
  407. result[u_quota.resource] = u_quota.hard_limit
  408. return result
  409. @require_context
  410. def quota_get_all_by_project_and_share_type(context, project_id,
  411. share_type_id):
  412. authorize_project_context(context, project_id)
  413. share_type_quotas = model_query(
  414. context, models.ProjectShareTypeQuota,
  415. models.ProjectShareTypeQuota.resource,
  416. models.ProjectShareTypeQuota.hard_limit,
  417. ).filter_by(
  418. project_id=project_id,
  419. ).filter_by(
  420. share_type_id=share_type_id,
  421. ).all()
  422. result = {
  423. 'project_id': project_id,
  424. 'share_type_id': share_type_id,
  425. }
  426. for st_quota in share_type_quotas:
  427. result[st_quota.resource] = st_quota.hard_limit
  428. return result
  429. @require_context
  430. def quota_get_all_by_project(context, project_id):
  431. authorize_project_context(context, project_id)
  432. project_quotas = model_query(
  433. context, models.Quota, read_deleted="no",
  434. ).filter_by(
  435. project_id=project_id,
  436. ).all()
  437. result = {'project_id': project_id}
  438. for p_quota in project_quotas:
  439. result[p_quota.resource] = p_quota.hard_limit
  440. return result
  441. @require_context
  442. def quota_get_all(context, project_id):
  443. authorize_project_context(context, project_id)
  444. result = (model_query(context, models.ProjectUserQuota).
  445. filter_by(project_id=project_id).
  446. all())
  447. return result
  448. @require_admin_context
  449. def quota_create(context, project_id, resource, limit, user_id=None,
  450. share_type_id=None):
  451. per_user = user_id and resource not in PER_PROJECT_QUOTAS
  452. if per_user:
  453. check = model_query(context, models.ProjectUserQuota).filter(
  454. models.ProjectUserQuota.project_id == project_id,
  455. models.ProjectUserQuota.user_id == user_id,
  456. models.ProjectUserQuota.resource == resource,
  457. ).all()
  458. quota_ref = models.ProjectUserQuota()
  459. quota_ref.user_id = user_id
  460. elif share_type_id:
  461. check = model_query(context, models.ProjectShareTypeQuota).filter(
  462. models.ProjectShareTypeQuota.project_id == project_id,
  463. models.ProjectShareTypeQuota.share_type_id == share_type_id,
  464. models.ProjectShareTypeQuota.resource == resource,
  465. ).all()
  466. quota_ref = models.ProjectShareTypeQuota()
  467. quota_ref.share_type_id = share_type_id
  468. else:
  469. check = model_query(context, models.Quota).filter(
  470. models.Quota.project_id == project_id,
  471. models.Quota.resource == resource,
  472. ).all()
  473. quota_ref = models.Quota()
  474. if check:
  475. raise exception.QuotaExists(project_id=project_id, resource=resource)
  476. quota_ref.project_id = project_id
  477. quota_ref.resource = resource
  478. quota_ref.hard_limit = limit
  479. session = get_session()
  480. with session.begin():
  481. quota_ref.save(session)
  482. return quota_ref
  483. @require_admin_context
  484. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  485. def quota_update(context, project_id, resource, limit, user_id=None,
  486. share_type_id=None):
  487. per_user = user_id and resource not in PER_PROJECT_QUOTAS
  488. if per_user:
  489. query = model_query(context, models.ProjectUserQuota).filter(
  490. models.ProjectUserQuota.project_id == project_id,
  491. models.ProjectUserQuota.user_id == user_id,
  492. models.ProjectUserQuota.resource == resource,
  493. )
  494. elif share_type_id:
  495. query = model_query(context, models.ProjectShareTypeQuota).filter(
  496. models.ProjectShareTypeQuota.project_id == project_id,
  497. models.ProjectShareTypeQuota.share_type_id == share_type_id,
  498. models.ProjectShareTypeQuota.resource == resource,
  499. )
  500. else:
  501. query = model_query(context, models.Quota).filter(
  502. models.Quota.project_id == project_id,
  503. models.Quota.resource == resource,
  504. )
  505. result = query.update({'hard_limit': limit})
  506. if not result:
  507. if per_user:
  508. raise exception.ProjectUserQuotaNotFound(
  509. project_id=project_id, user_id=user_id)
  510. elif share_type_id:
  511. raise exception.ProjectShareTypeQuotaNotFound(
  512. project_id=project_id, share_type=share_type_id)
  513. raise exception.ProjectQuotaNotFound(project_id=project_id)
  514. ###################
  515. @require_context
  516. def quota_class_get(context, class_name, resource, session=None):
  517. result = (model_query(context, models.QuotaClass, session=session,
  518. read_deleted="no").
  519. filter_by(class_name=class_name).
  520. filter_by(resource=resource).
  521. first())
  522. if not result:
  523. raise exception.QuotaClassNotFound(class_name=class_name)
  524. return result
  525. @require_context
  526. def quota_class_get_default(context):
  527. rows = (model_query(context, models.QuotaClass, read_deleted="no").
  528. filter_by(class_name=_DEFAULT_QUOTA_NAME).
  529. all())
  530. result = {'class_name': _DEFAULT_QUOTA_NAME}
  531. for row in rows:
  532. result[row.resource] = row.hard_limit
  533. return result
  534. @require_context
  535. def quota_class_get_all_by_name(context, class_name):
  536. authorize_quota_class_context(context, class_name)
  537. rows = (model_query(context, models.QuotaClass, read_deleted="no").
  538. filter_by(class_name=class_name).
  539. all())
  540. result = {'class_name': class_name}
  541. for row in rows:
  542. result[row.resource] = row.hard_limit
  543. return result
  544. @require_admin_context
  545. def quota_class_create(context, class_name, resource, limit):
  546. quota_class_ref = models.QuotaClass()
  547. quota_class_ref.class_name = class_name
  548. quota_class_ref.resource = resource
  549. quota_class_ref.hard_limit = limit
  550. session = get_session()
  551. with session.begin():
  552. quota_class_ref.save(session)
  553. return quota_class_ref
  554. @require_admin_context
  555. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  556. def quota_class_update(context, class_name, resource, limit):
  557. result = (model_query(context, models.QuotaClass, read_deleted="no").
  558. filter_by(class_name=class_name).
  559. filter_by(resource=resource).
  560. update({'hard_limit': limit}))
  561. if not result:
  562. raise exception.QuotaClassNotFound(class_name=class_name)
  563. ###################
  564. @require_context
  565. def quota_usage_get(context, project_id, resource, user_id=None,
  566. share_type_id=None):
  567. query = (model_query(context, models.QuotaUsage, read_deleted="no").
  568. filter_by(project_id=project_id).
  569. filter_by(resource=resource))
  570. if user_id:
  571. if resource not in PER_PROJECT_QUOTAS:
  572. result = query.filter_by(user_id=user_id).first()
  573. else:
  574. result = query.filter_by(user_id=None).first()
  575. elif share_type_id:
  576. result = query.filter_by(queryshare_type_id=share_type_id).first()
  577. else:
  578. result = query.first()
  579. if not result:
  580. raise exception.QuotaUsageNotFound(project_id=project_id)
  581. return result
  582. def _quota_usage_get_all(context, project_id, user_id=None,
  583. share_type_id=None):
  584. authorize_project_context(context, project_id)
  585. query = (model_query(context, models.QuotaUsage, read_deleted="no").
  586. filter_by(project_id=project_id))
  587. result = {'project_id': project_id}
  588. if user_id:
  589. query = query.filter(or_(models.QuotaUsage.user_id == user_id,
  590. models.QuotaUsage.user_id is None))
  591. result['user_id'] = user_id
  592. elif share_type_id:
  593. query = query.filter_by(share_type_id=share_type_id)
  594. result['share_type_id'] = share_type_id
  595. else:
  596. query = query.filter_by(share_type_id=None)
  597. rows = query.all()
  598. for row in rows:
  599. if row.resource in result:
  600. result[row.resource]['in_use'] += row.in_use
  601. result[row.resource]['reserved'] += row.reserved
  602. else:
  603. result[row.resource] = dict(in_use=row.in_use,
  604. reserved=row.reserved)
  605. return result
  606. @require_context
  607. def quota_usage_get_all_by_project(context, project_id):
  608. return _quota_usage_get_all(context, project_id)
  609. @require_context
  610. def quota_usage_get_all_by_project_and_user(context, project_id, user_id):
  611. return _quota_usage_get_all(context, project_id, user_id=user_id)
  612. @require_context
  613. def quota_usage_get_all_by_project_and_share_type(context, project_id,
  614. share_type_id):
  615. return _quota_usage_get_all(
  616. context, project_id, share_type_id=share_type_id)
  617. def _quota_usage_create(context, project_id, user_id, resource, in_use,
  618. reserved, until_refresh, share_type_id=None,
  619. session=None):
  620. quota_usage_ref = models.QuotaUsage()
  621. if share_type_id:
  622. quota_usage_ref.share_type_id = share_type_id
  623. else:
  624. quota_usage_ref.user_id = user_id
  625. quota_usage_ref.project_id = project_id
  626. quota_usage_ref.resource = resource
  627. quota_usage_ref.in_use = in_use
  628. quota_usage_ref.reserved = reserved
  629. quota_usage_ref.until_refresh = until_refresh
  630. # updated_at is needed for judgement of max_age
  631. quota_usage_ref.updated_at = timeutils.utcnow()
  632. quota_usage_ref.save(session=session)
  633. return quota_usage_ref
  634. @require_admin_context
  635. def quota_usage_create(context, project_id, user_id, resource, in_use,
  636. reserved, until_refresh, share_type_id=None):
  637. session = get_session()
  638. return _quota_usage_create(
  639. context, project_id, user_id, resource, in_use, reserved,
  640. until_refresh, share_type_id=share_type_id, session=session)
  641. @require_admin_context
  642. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  643. def quota_usage_update(context, project_id, user_id, resource,
  644. share_type_id=None, **kwargs):
  645. updates = {}
  646. for key in ('in_use', 'reserved', 'until_refresh'):
  647. if key in kwargs:
  648. updates[key] = kwargs[key]
  649. query = model_query(
  650. context, models.QuotaUsage, read_deleted="no",
  651. ).filter_by(project_id=project_id).filter_by(resource=resource)
  652. if share_type_id:
  653. query = query.filter_by(share_type_id=share_type_id)
  654. else:
  655. query = query.filter(or_(models.QuotaUsage.user_id == user_id,
  656. models.QuotaUsage.user_id is None))
  657. result = query.update(updates)
  658. if not result:
  659. raise exception.QuotaUsageNotFound(project_id=project_id)
  660. ###################
  661. def _reservation_create(context, uuid, usage, project_id, user_id, resource,
  662. delta, expire, share_type_id=None, session=None):
  663. reservation_ref = models.Reservation()
  664. reservation_ref.uuid = uuid
  665. reservation_ref.usage_id = usage['id']
  666. reservation_ref.project_id = project_id
  667. if share_type_id:
  668. reservation_ref.share_type_id = share_type_id
  669. else:
  670. reservation_ref.user_id = user_id
  671. reservation_ref.resource = resource
  672. reservation_ref.delta = delta
  673. reservation_ref.expire = expire
  674. reservation_ref.save(session=session)
  675. return reservation_ref
  676. ###################
  677. # NOTE(johannes): The quota code uses SQL locking to ensure races don't
  678. # cause under or over counting of resources. To avoid deadlocks, this
  679. # code always acquires the lock on quota_usages before acquiring the lock
  680. # on reservations.
  681. def _get_share_type_quota_usages(context, session, project_id, share_type_id):
  682. rows = model_query(
  683. context, models.QuotaUsage, read_deleted="no", session=session,
  684. ).filter(
  685. models.QuotaUsage.project_id == project_id,
  686. models.QuotaUsage.share_type_id == share_type_id,
  687. ).with_lockmode('update').all()
  688. return {row.resource: row for row in rows}
  689. def _get_user_quota_usages(context, session, project_id, user_id):
  690. # Broken out for testability
  691. rows = (model_query(context, models.QuotaUsage,
  692. read_deleted="no",
  693. session=session).
  694. filter_by(project_id=project_id).
  695. filter(or_(models.QuotaUsage.user_id == user_id,
  696. models.QuotaUsage.user_id is None)).
  697. with_lockmode('update').
  698. all())
  699. return {row.resource: row for row in rows}
  700. def _get_project_quota_usages(context, session, project_id):
  701. rows = (model_query(context, models.QuotaUsage,
  702. read_deleted="no",
  703. session=session).
  704. filter_by(project_id=project_id).
  705. filter(models.QuotaUsage.share_type_id is None).
  706. with_lockmode('update').
  707. all())
  708. result = dict()
  709. # Get the total count of in_use,reserved
  710. for row in rows:
  711. if row.resource in result:
  712. result[row.resource]['in_use'] += row.in_use
  713. result[row.resource]['reserved'] += row.reserved
  714. result[row.resource]['total'] += (row.in_use + row.reserved)
  715. else:
  716. result[row.resource] = dict(in_use=row.in_use,
  717. reserved=row.reserved,
  718. total=row.in_use + row.reserved)
  719. return result
  720. @require_context
  721. def quota_reserve(context, resources, project_quotas, user_quotas,
  722. share_type_quotas, deltas, expire, until_refresh,
  723. max_age, project_id=None, user_id=None, share_type_id=None):
  724. user_reservations = _quota_reserve(
  725. context, resources, project_quotas, user_quotas,
  726. deltas, expire, until_refresh, max_age, project_id, user_id=user_id)
  727. if share_type_id:
  728. try:
  729. st_reservations = _quota_reserve(
  730. context, resources, project_quotas, share_type_quotas,
  731. deltas, expire, until_refresh, max_age, project_id,
  732. share_type_id=share_type_id)
  733. except exception.OverQuota:
  734. with excutils.save_and_reraise_exception():
  735. # rollback previous reservations
  736. reservation_rollback(
  737. context, user_reservations,
  738. project_id=project_id, user_id=user_id)
  739. return user_reservations + st_reservations
  740. return user_reservations
  741. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  742. def _quota_reserve(context, resources, project_quotas, user_or_st_quotas,
  743. deltas, expire, until_refresh,
  744. max_age, project_id=None, user_id=None, share_type_id=None):
  745. elevated = context.elevated()
  746. session = get_session()
  747. with session.begin():
  748. if project_id is None:
  749. project_id = context.project_id
  750. if share_type_id:
  751. user_or_st_usages = _get_share_type_quota_usages(
  752. context, session, project_id, share_type_id)
  753. else:
  754. user_id = user_id if user_id else context.user_id
  755. user_or_st_usages = _get_user_quota_usages(
  756. context, session, project_id, user_id)
  757. # Get the current usages
  758. project_usages = _get_project_quota_usages(
  759. context, session, project_id)
  760. # Handle usage refresh
  761. work = set(deltas.keys())
  762. while work:
  763. resource = work.pop()
  764. # Do we need to refresh the usage?
  765. refresh = False
  766. if ((resource not in PER_PROJECT_QUOTAS) and
  767. (resource not in user_or_st_usages)):
  768. user_or_st_usages[resource] = _quota_usage_create(
  769. elevated,
  770. project_id,
  771. user_id,
  772. resource,
  773. 0, 0,
  774. until_refresh or None,
  775. share_type_id=share_type_id,
  776. session=session)
  777. refresh = True
  778. elif ((resource in PER_PROJECT_QUOTAS) and
  779. (resource not in user_or_st_usages)):
  780. user_or_st_usages[resource] = _quota_usage_create(
  781. elevated,
  782. project_id,
  783. None,
  784. resource,
  785. 0, 0,
  786. until_refresh or None,
  787. share_type_id=share_type_id,
  788. session=session)
  789. refresh = True
  790. elif user_or_st_usages[resource].in_use < 0:
  791. # Negative in_use count indicates a desync, so try to
  792. # heal from that...
  793. refresh = True
  794. elif user_or_st_usages[resource].until_refresh is not None:
  795. user_or_st_usages[resource].until_refresh -= 1
  796. if user_or_st_usages[resource].until_refresh <= 0:
  797. refresh = True
  798. elif max_age and (user_or_st_usages[resource].updated_at -
  799. timeutils.utcnow()).seconds >= max_age:
  800. refresh = True
  801. # OK, refresh the usage
  802. if refresh:
  803. # Grab the sync routine
  804. sync = QUOTA_SYNC_FUNCTIONS[resources[resource].sync]
  805. updates = sync(
  806. elevated, project_id, user_id,
  807. share_type_id=share_type_id, session=session)
  808. for res, in_use in updates.items():
  809. # Make sure we have a destination for the usage!
  810. if ((res not in PER_PROJECT_QUOTAS) and
  811. (res not in user_or_st_usages)):
  812. user_or_st_usages[res] = _quota_usage_create(
  813. elevated,
  814. project_id,
  815. user_id,
  816. res,
  817. 0, 0,
  818. until_refresh or None,
  819. share_type_id=share_type_id,
  820. session=session)
  821. if ((res in PER_PROJECT_QUOTAS) and
  822. (res not in user_or_st_usages)):
  823. user_or_st_usages[res] = _quota_usage_create(
  824. elevated,
  825. project_id,
  826. None,
  827. res,
  828. 0, 0,
  829. until_refresh or None,
  830. share_type_id=share_type_id,
  831. session=session)
  832. if user_or_st_usages[res].in_use != in_use:
  833. LOG.debug(
  834. 'quota_usages out of sync, updating. '
  835. 'project_id: %(project_id)s, '
  836. 'user_id: %(user_id)s, '
  837. 'share_type_id: %(share_type_id)s, '
  838. 'resource: %(res)s, '
  839. 'tracked usage: %(tracked_use)s, '
  840. 'actual usage: %(in_use)s',
  841. {'project_id': project_id,
  842. 'user_id': user_id,
  843. 'share_type_id': share_type_id,
  844. 'res': res,
  845. 'tracked_use': user_or_st_usages[res].in_use,
  846. 'in_use': in_use})
  847. # Update the usage
  848. user_or_st_usages[res].in_use = in_use
  849. user_or_st_usages[res].until_refresh = (
  850. until_refresh or None)
  851. # Because more than one resource may be refreshed
  852. # by the call to the sync routine, and we don't
  853. # want to double-sync, we make sure all refreshed
  854. # resources are dropped from the work set.
  855. work.discard(res)
  856. # NOTE(Vek): We make the assumption that the sync
  857. # routine actually refreshes the
  858. # resources that it is the sync routine
  859. # for. We don't check, because this is
  860. # a best-effort mechanism.
  861. # Check for deltas that would go negative
  862. unders = [res for res, delta in deltas.items()
  863. if delta < 0 and
  864. delta + user_or_st_usages[res].in_use < 0]
  865. # Now, let's check the quotas
  866. # NOTE(Vek): We're only concerned about positive increments.
  867. # If a project has gone over quota, we want them to
  868. # be able to reduce their usage without any
  869. # problems.
  870. for key, value in user_or_st_usages.items():
  871. if key not in project_usages:
  872. project_usages[key] = value
  873. overs = [res for res, delta in deltas.items()
  874. if user_or_st_quotas[res] >= 0 and delta >= 0 and
  875. (project_quotas[res] < delta +
  876. project_usages[res]['total'] or
  877. user_or_st_quotas[res] < delta +
  878. user_or_st_usages[res].total)]
  879. # NOTE(Vek): The quota check needs to be in the transaction,
  880. # but the transaction doesn't fail just because
  881. # we're over quota, so the OverQuota raise is
  882. # outside the transaction. If we did the raise
  883. # here, our usage updates would be discarded, but
  884. # they're not invalidated by being over-quota.
  885. # Create the reservations
  886. if not overs:
  887. reservations = []
  888. for res, delta in deltas.items():
  889. reservation = _reservation_create(elevated,
  890. uuidutils.generate_uuid(),
  891. user_or_st_usages[res],
  892. project_id,
  893. user_id,
  894. res, delta, expire,
  895. share_type_id=share_type_id,
  896. session=session)
  897. reservations.append(reservation.uuid)
  898. # Also update the reserved quantity
  899. # NOTE(Vek): Again, we are only concerned here about
  900. # positive increments. Here, though, we're
  901. # worried about the following scenario:
  902. #
  903. # 1) User initiates resize down.
  904. # 2) User allocates a new instance.
  905. # 3) Resize down fails or is reverted.
  906. # 4) User is now over quota.
  907. #
  908. # To prevent this, we only update the
  909. # reserved value if the delta is positive.
  910. if delta > 0:
  911. user_or_st_usages[res].reserved += delta
  912. # Apply updates to the usages table
  913. for usage_ref in user_or_st_usages.values():
  914. session.add(usage_ref)
  915. if unders:
  916. LOG.warning("Change will make usage less than 0 for the following "
  917. "resources: %s", unders)
  918. if overs:
  919. if project_quotas == user_or_st_quotas:
  920. usages = project_usages
  921. else:
  922. usages = user_or_st_usages
  923. usages = {k: dict(in_use=v['in_use'], reserved=v['reserved'])
  924. for k, v in usages.items()}
  925. raise exception.OverQuota(
  926. overs=sorted(overs), quotas=user_or_st_quotas, usages=usages)
  927. return reservations
  928. def _quota_reservations_query(session, context, reservations):
  929. """Return the relevant reservations."""
  930. # Get the listed reservations
  931. return (model_query(context, models.Reservation,
  932. read_deleted="no",
  933. session=session).
  934. filter(models.Reservation.uuid.in_(reservations)).
  935. with_lockmode('update'))
  936. @require_context
  937. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  938. def reservation_commit(context, reservations, project_id=None, user_id=None,
  939. share_type_id=None):
  940. session = get_session()
  941. with session.begin():
  942. if share_type_id:
  943. st_usages = _get_share_type_quota_usages(
  944. context, session, project_id, share_type_id)
  945. else:
  946. st_usages = {}
  947. user_usages = _get_user_quota_usages(
  948. context, session, project_id, user_id)
  949. reservation_query = _quota_reservations_query(
  950. session, context, reservations)
  951. for reservation in reservation_query.all():
  952. if reservation['share_type_id']:
  953. usages = st_usages
  954. else:
  955. usages = user_usages
  956. usage = usages[reservation.resource]
  957. if reservation.delta >= 0:
  958. usage.reserved -= reservation.delta
  959. usage.in_use += reservation.delta
  960. reservation_query.soft_delete(synchronize_session=False)
  961. @require_context
  962. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  963. def reservation_rollback(context, reservations, project_id=None, user_id=None,
  964. share_type_id=None):
  965. session = get_session()
  966. with session.begin():
  967. if share_type_id:
  968. st_usages = _get_share_type_quota_usages(
  969. context, session, project_id, share_type_id)
  970. else:
  971. st_usages = {}
  972. user_usages = _get_user_quota_usages(
  973. context, session, project_id, user_id)
  974. reservation_query = _quota_reservations_query(
  975. session, context, reservations)
  976. for reservation in reservation_query.all():
  977. if reservation['share_type_id']:
  978. usages = st_usages
  979. else:
  980. usages = user_usages
  981. usage = usages[reservation.resource]
  982. if reservation.delta >= 0:
  983. usage.reserved -= reservation.delta
  984. reservation_query.soft_delete(synchronize_session=False)
  985. @require_admin_context
  986. def quota_destroy_all_by_project_and_user(context, project_id, user_id):
  987. session = get_session()
  988. with session.begin():
  989. (model_query(context, models.ProjectUserQuota, session=session,
  990. read_deleted="no").
  991. filter_by(project_id=project_id).
  992. filter_by(user_id=user_id).soft_delete(synchronize_session=False))
  993. (model_query(context, models.QuotaUsage,
  994. session=session, read_deleted="no").
  995. filter_by(project_id=project_id).
  996. filter_by(user_id=user_id).soft_delete(synchronize_session=False))
  997. (model_query(context, models.Reservation,
  998. session=session, read_deleted="no").
  999. filter_by(project_id=project_id).
  1000. filter_by(user_id=user_id).soft_delete(synchronize_session=False))
  1001. @require_admin_context
  1002. def quota_destroy_all_by_share_type(context, share_type_id, project_id=None):
  1003. """Soft deletes all quotas, usages and reservations.
  1004. :param context: request context for queries, updates and logging
  1005. :param share_type_id: ID of the share type to filter the quotas, usages
  1006. and reservations under.
  1007. :param project_id: ID of the project to filter the quotas, usages and
  1008. reservations under. If not provided, share type quotas for all
  1009. projects will be acted upon.
  1010. """
  1011. session = get_session()
  1012. with session.begin():
  1013. share_type_quotas = model_query(
  1014. context, models.ProjectShareTypeQuota, session=session,
  1015. read_deleted="no",
  1016. ).filter_by(share_type_id=share_type_id)
  1017. share_type_quota_usages = model_query(
  1018. context, models.QuotaUsage, session=session, read_deleted="no",
  1019. ).filter_by(share_type_id=share_type_id)
  1020. share_type_quota_reservations = model_query(
  1021. context, models.Reservation, session=session, read_deleted="no",
  1022. ).filter_by(share_type_id=share_type_id)
  1023. if project_id is not None:
  1024. share_type_quotas = share_type_quotas.filter_by(
  1025. project_id=project_id)
  1026. share_type_quota_usages = share_type_quota_usages.filter_by(
  1027. project_id=project_id)
  1028. share_type_quota_reservations = (
  1029. share_type_quota_reservations.filter_by(project_id=project_id))
  1030. share_type_quotas.soft_delete(synchronize_session=False)
  1031. share_type_quota_usages.soft_delete(synchronize_session=False)
  1032. share_type_quota_reservations.soft_delete(synchronize_session=False)
  1033. @require_admin_context
  1034. def quota_destroy_all_by_project(context, project_id):
  1035. session = get_session()
  1036. with session.begin():
  1037. (model_query(context, models.Quota, session=session,
  1038. read_deleted="no").
  1039. filter_by(project_id=project_id).
  1040. soft_delete(synchronize_session=False))
  1041. (model_query(context, models.ProjectUserQuota, session=session,
  1042. read_deleted="no").
  1043. filter_by(project_id=project_id).
  1044. soft_delete(synchronize_session=False))
  1045. (model_query(context, models.QuotaUsage,
  1046. session=session, read_deleted="no").
  1047. filter_by(project_id=project_id).
  1048. soft_delete(synchronize_session=False))
  1049. (model_query(context, models.Reservation,
  1050. session=session, read_deleted="no").
  1051. filter_by(project_id=project_id).
  1052. soft_delete(synchronize_session=False))
  1053. @require_admin_context
  1054. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  1055. def reservation_expire(context):
  1056. session = get_session()
  1057. with session.begin():
  1058. current_time = timeutils.utcnow()
  1059. reservation_query = (model_query(
  1060. context, models.Reservation,
  1061. session=session, read_deleted="no").
  1062. filter(models.Reservation.expire < current_time))
  1063. for reservation in reservation_query.all():
  1064. if reservation.delta >= 0:
  1065. quota_usage = model_query(context, models.QuotaUsage,
  1066. session=session,
  1067. read_deleted="no").filter(
  1068. models.QuotaUsage.id == reservation.usage_id).first()
  1069. quota_usage.reserved -= reservation.delta
  1070. session.add(quota_usage)
  1071. reservation_query.soft_delete(synchronize_session=False)
  1072. ################
  1073. def _extract_subdict_by_fields(source_dict, fields):
  1074. dict_to_extract_from = copy.deepcopy(source_dict)
  1075. sub_dict = {}
  1076. for field in fields:
  1077. field_value = dict_to_extract_from.pop(field, None)
  1078. if field_value:
  1079. sub_dict.update({field: field_value})
  1080. return sub_dict, dict_to_extract_from
  1081. def _extract_share_instance_values(values):
  1082. share_instance_model_fields = [
  1083. 'status', 'host', 'scheduled_at', 'launched_at', 'terminated_at',
  1084. 'share_server_id', 'share_network_id', 'availability_zone',
  1085. 'replica_state', 'share_type_id', 'share_type', 'access_rules_status',
  1086. ]
  1087. share_instance_values, share_values = (
  1088. _extract_subdict_by_fields(values, share_instance_model_fields)
  1089. )
  1090. return share_instance_values, share_values
  1091. def _change_size_to_instance_size(snap_instance_values):
  1092. if 'size' in snap_instance_values:
  1093. snap_instance_values['instance_size'] = snap_instance_values['size']
  1094. snap_instance_values.pop('size')
  1095. def _extract_snapshot_instance_values(values):
  1096. fields = ['status', 'progress', 'provider_location']
  1097. snapshot_instance_values, snapshot_values = (
  1098. _extract_subdict_by_fields(values, fields)
  1099. )
  1100. return snapshot_instance_values, snapshot_values
  1101. ################
  1102. @require_context
  1103. def share_instance_create(context, share_id, values):
  1104. session = get_session()
  1105. with session.begin():
  1106. return _share_instance_create(context, share_id, values, session)
  1107. def _share_instance_create(context, share_id, values, session):
  1108. if not values.get('id'):
  1109. values['id'] = uuidutils.generate_uuid()
  1110. values.update({'share_id': share_id})
  1111. share_instance_ref = models.ShareInstance()
  1112. share_instance_ref.update(values)
  1113. share_instance_ref.save(session=session)
  1114. return share_instance_get(context, share_instance_ref['id'],
  1115. session=session)
  1116. @require_admin_context
  1117. def share_instances_host_update(context, current_host, new_host):
  1118. session = get_session()
  1119. host_field = models.ShareInstance.host
  1120. with session.begin():
  1121. query = model_query(
  1122. context, models.ShareInstance, session=session, read_deleted="no",
  1123. ).filter(host_field.like('{}%'.format(current_host)))
  1124. result = query.update(
  1125. {host_field: func.replace(host_field, current_host, new_host)},
  1126. synchronize_session=False)
  1127. return result
  1128. @require_context
  1129. def share_instance_update(context, share_instance_id, values,
  1130. with_share_data=False):
  1131. session = get_session()
  1132. _ensure_availability_zone_exists(context, values, session, strict=False)
  1133. with session.begin():
  1134. instance_ref = _share_instance_update(
  1135. context, share_instance_id, values, session
  1136. )
  1137. if with_share_data:
  1138. parent_share = share_get(context, instance_ref['share_id'],
  1139. session=session)
  1140. instance_ref.set_share_data(parent_share)
  1141. return instance_ref
  1142. def _share_instance_update(context, share_instance_id, values, session):
  1143. share_instance_ref = share_instance_get(context, share_instance_id,
  1144. session=session)
  1145. share_instance_ref.update(values)
  1146. share_instance_ref.save(session=session)
  1147. return share_instance_ref
  1148. @require_context
  1149. def share_instance_get(context, share_instance_id, session=None,
  1150. with_share_data=False):
  1151. if session is None:
  1152. session = get_session()
  1153. result = model_query(
  1154. context, models.ShareInstance, session=session,
  1155. ).filter_by(
  1156. id=share_instance_id,
  1157. ).options(
  1158. joinedload('export_locations'),
  1159. joinedload('share_type'),
  1160. ).first()
  1161. if result is None:
  1162. raise exception.NotFound()
  1163. if with_share_data:
  1164. parent_share = share_get(context, result['share_id'], session=session)
  1165. result.set_share_data(parent_share)
  1166. return result
  1167. @require_admin_context
  1168. def share_instances_get_all(context, filters=None):
  1169. session = get_session()
  1170. query = model_query(
  1171. context, models.ShareInstance, session=session, read_deleted="no",
  1172. ).options(
  1173. joinedload('export_locations'),
  1174. )
  1175. filters = filters or {}
  1176. export_location_id = filters.get('export_location_id')
  1177. export_location_path = filters.get('export_location_path')
  1178. if export_location_id or export_location_path:
  1179. query = query.join(
  1180. models.ShareInstanceExportLocations,
  1181. models.ShareInstanceExportLocations.share_instance_id ==
  1182. models.ShareInstance.id)
  1183. if export_location_path:
  1184. query = query.filter(
  1185. models.ShareInstanceExportLocations.path ==
  1186. export_location_path)
  1187. if export_location_id:
  1188. query = query.filter(
  1189. models.ShareInstanceExportLocations.uuid ==
  1190. export_location_id)
  1191. # Returns list of share instances that satisfy filters.
  1192. query = query.all()
  1193. return query
  1194. @require_context
  1195. def share_instance_delete(context, instance_id, session=None,
  1196. need_to_update_usages=False):
  1197. if session is None:
  1198. session = get_session()
  1199. with session.begin():
  1200. share_export_locations_update(context, instance_id, [], delete=True)
  1201. instance_ref = share_instance_get(context, instance_id,
  1202. session=session)
  1203. instance_ref.soft_delete(session=session, update_status=True)
  1204. share = share_get(context, instance_ref['share_id'], session=session)
  1205. if len(share.instances) == 0:
  1206. share_access_delete_all_by_share(context, share['id'])
  1207. session.query(models.ShareMetadata).filter_by(
  1208. share_id=share['id']).soft_delete()
  1209. share.soft_delete(session=session)
  1210. if need_to_update_usages:
  1211. reservations = None
  1212. try:
  1213. # we give the user_id of the share, to update
  1214. # the quota usage for the user, who created the share
  1215. reservations = QUOTAS.reserve(
  1216. context,
  1217. project_id=share['project_id'],
  1218. shares=-1,
  1219. gigabytes=-share['size'],
  1220. user_id=share['user_id'],
  1221. share_type_id=instance_ref['share_type_id'])
  1222. QUOTAS.commit(
  1223. context, reservations, project_id=share['project_id'],
  1224. user_id=share['user_id'],
  1225. share_type_id=instance_ref['share_type_id'])
  1226. except Exception:
  1227. LOG.exception(
  1228. "Failed to update usages deleting share '%s'.",
  1229. share["id"])
  1230. if reservations:
  1231. QUOTAS.rollback(
  1232. context, reservations,
  1233. share_type_id=instance_ref['share_type_id'])
  1234. def _set_instances_share_data(context, instances, session):
  1235. if instances and not isinstance(instances, list):
  1236. instances = [instances]
  1237. instances_with_share_data = []
  1238. for instance in instances:
  1239. try:
  1240. parent_share = share_get(context, instance['share_id'],
  1241. session=session)
  1242. except exception.NotFound:
  1243. continue
  1244. instance.set_share_data(parent_share)
  1245. instances_with_share_data.append(instance)
  1246. return instances_with_share_data
  1247. @require_admin_context
  1248. def share_instances_get_all_by_host(context, host, with_share_data=False,
  1249. session=None):
  1250. """Retrieves all share instances hosted on a host."""
  1251. session = session or get_session()
  1252. instances = (
  1253. model_query(context, models.ShareInstance).filter(
  1254. or_(
  1255. models.ShareInstance.host == host,
  1256. models.ShareInstance.host.like("{0}#%".format(host))
  1257. )
  1258. ).all()
  1259. )
  1260. if with_share_data:
  1261. instances = _set_instances_share_data(context, instances, session)
  1262. return instances
  1263. @require_context
  1264. def share_instances_get_all_by_share_network(context, share_network_id):
  1265. """Returns list of share instances that belong to given share network."""
  1266. result = (
  1267. model_query(context, models.ShareInstance).filter(
  1268. models.ShareInstance.share_network_id == share_network_id,
  1269. ).all()
  1270. )
  1271. return result
  1272. @require_context
  1273. def share_instances_get_all_by_share_server(context, share_server_id):
  1274. """Returns list of share instance with given share server."""
  1275. result = (
  1276. model_query(context, models.ShareInstance).filter(
  1277. models.ShareInstance.share_server_id == share_server_id,
  1278. ).all()
  1279. )
  1280. return result
  1281. @require_context
  1282. def share_instances_get_all_by_share(context, share_id):
  1283. """Returns list of share instances that belong to given share."""
  1284. result = (
  1285. model_query(context, models.ShareInstance).filter(
  1286. models.ShareInstance.share_id == share_id,
  1287. ).all()
  1288. )
  1289. return result
  1290. @require_context
  1291. def share_instances_get_all_by_share_group_id(context, share_group_id):
  1292. """Returns list of share instances that belong to given share group."""
  1293. result = (
  1294. model_query(context, models.Share).filter(
  1295. models.Share.share_group_id == share_group_id,
  1296. ).all()
  1297. )
  1298. instances = []
  1299. for share in result:
  1300. instance = share.instance
  1301. instance.set_share_data(share)
  1302. instances.append(instance)
  1303. return instances
  1304. ################
  1305. def _share_replica_get_with_filters(context, share_id=None, replica_id=None,
  1306. replica_state=None, status=None,
  1307. with_share_server=True, session=None):
  1308. query = model_query(context, models.ShareInstance, session=session,
  1309. read_deleted="no")
  1310. if share_id is not None:
  1311. query = query.filter(models.ShareInstance.share_id == share_id)
  1312. if replica_id is not None:
  1313. query = query.filter(models.ShareInstance.id == replica_id)
  1314. if replica_state is not None:
  1315. query = query.filter(
  1316. models.ShareInstance.replica_state == replica_state)
  1317. else:
  1318. query = query.filter(models.ShareInstance.replica_state.isnot(None))
  1319. if status is not None:
  1320. query = query.filter(models.ShareInstance.status == status)
  1321. if with_share_server:
  1322. query = query.options(joinedload('share_server'))
  1323. return query
  1324. def _set_replica_share_data(context, replicas, session):
  1325. if replicas and not isinstance(replicas, list):
  1326. replicas = [replicas]
  1327. for replica in replicas:
  1328. parent_share = share_get(context, replica['share_id'], session=session)
  1329. replica.set_share_data(parent_share)
  1330. return replicas
  1331. @require_context
  1332. def share_replicas_get_all(context, with_share_data=False,
  1333. with_share_server=True, session=None):
  1334. """Returns replica instances for all available replicated shares."""
  1335. session = session or get_session()
  1336. result = _share_replica_get_with_filters(
  1337. context, with_share_server=with_share_server, session=session).all()
  1338. if with_share_data:
  1339. result = _set_replica_share_data(context, result, session)
  1340. return result
  1341. @require_context
  1342. def share_replicas_get_all_by_share(context, share_id,
  1343. with_share_data=False,
  1344. with_share_server=False, session=None):
  1345. """Returns replica instances for a given share."""
  1346. session = session or get_session()
  1347. result = _share_replica_get_with_filters(
  1348. context, with_share_server=with_share_server,
  1349. share_id=share_id, session=session).all()
  1350. if with_share_data:
  1351. result = _set_replica_share_data(context, result, session)
  1352. return result
  1353. @require_context
  1354. def share_replicas_get_available_active_replica(context, share_id,
  1355. with_share_data=False,
  1356. with_share_server=False,
  1357. session=None):
  1358. """Returns an 'active' replica instance that is 'available'."""
  1359. session = session or get_session()
  1360. result = _share_replica_get_with_filters(
  1361. context, with_share_server=with_share_server, share_id=share_id,
  1362. replica_state=constants.REPLICA_STATE_ACTIVE,
  1363. status=constants.STATUS_AVAILABLE, session=session).first()
  1364. if result and with_share_data:
  1365. result = _set_replica_share_data(context, result, session)[0]
  1366. return result
  1367. @require_context
  1368. def share_replica_get(context, replica_id, with_share_data=False,
  1369. with_share_server=False, session=None):
  1370. """Returns summary of requested replica if available."""
  1371. session = session or get_session()
  1372. result = _share_replica_get_with_filters(
  1373. context, with_share_server=with_share_server,
  1374. replica_id=replica_id, session=session).first()
  1375. if result is None:
  1376. raise exception.ShareReplicaNotFound(replica_id=replica_id)
  1377. if with_share_data:
  1378. result = _set_replica_share_data(context, result, session)[0]
  1379. return result
  1380. @require_context
  1381. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  1382. def share_replica_update(context, share_replica_id, values,
  1383. with_share_data=False, session=None):
  1384. """Updates a share replica with specified values."""
  1385. session = session or get_session()
  1386. with session.begin():
  1387. _ensure_availability_zone_exists(context, values, session,
  1388. strict=False)
  1389. updated_share_replica = _share_instance_update(
  1390. context, share_replica_id, values, session=session)
  1391. if with_share_data:
  1392. updated_share_replica = _set_replica_share_data(
  1393. context, updated_share_replica, session)[0]
  1394. return updated_share_replica
  1395. @require_context
  1396. def share_replica_delete(context, share_replica_id, session=None):
  1397. """Deletes a share replica."""
  1398. session = session or get_session()
  1399. share_instance_delete(context, share_replica_id, session=session)
  1400. ################
  1401. def _share_get_query(context, session=None):
  1402. if session is None:
  1403. session = get_session()
  1404. return (model_query(context, models.Share, session=session).
  1405. options(joinedload('share_metadata')))
  1406. def _metadata_refs(metadata_dict, meta_class):
  1407. metadata_refs = []
  1408. if metadata_dict:
  1409. for k, v in metadata_dict.items():
  1410. value = six.text_type(v) if isinstance(v, bool) else v
  1411. metadata_ref = meta_class()
  1412. metadata_ref['key'] = k
  1413. metadata_ref['value'] = value
  1414. metadata_refs.append(metadata_ref)
  1415. return metadata_refs
  1416. @require_context
  1417. def share_create(context, share_values, create_share_instance=True):
  1418. values = copy.deepcopy(share_values)
  1419. values = ensure_model_dict_has_id(values)
  1420. values['share_metadata'] = _metadata_refs(values.get('metadata'),
  1421. models.ShareMetadata)
  1422. session = get_session()
  1423. share_ref = models.Share()
  1424. share_instance_values, share_values = _extract_share_instance_values(
  1425. values)
  1426. _ensure_availability_zone_exists(context, share_instance_values, session,
  1427. strict=False)
  1428. share_ref.update(share_values)
  1429. with session.begin():
  1430. share_ref.save(session=session)
  1431. if create_share_instance:
  1432. _share_instance_create(context, share_ref['id'],
  1433. share_instance_values, session=session)
  1434. # NOTE(u_glide): Do so to prevent errors with relationships
  1435. return share_get(context, share_ref['id'], session=session)
  1436. @require_admin_context
  1437. def share_data_get_for_project(context, project_id, user_id,
  1438. share_type_id=None, session=None):
  1439. query = (model_query(context, models.Share,
  1440. func.count(models.Share.id),
  1441. func.sum(models.Share.size),
  1442. read_deleted="no",
  1443. session=session).
  1444. filter_by(project_id=project_id))
  1445. if share_type_id:
  1446. query = query.join("instances").filter_by(share_type_id=share_type_id)
  1447. elif user_id:
  1448. query = query.filter_by(user_id=user_id)
  1449. result = query.first()
  1450. return (result[0] or 0, result[1] or 0)
  1451. @require_context
  1452. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  1453. def share_update(context, share_id, update_values):
  1454. session = get_session()
  1455. values = copy.deepcopy(update_values)
  1456. share_instance_values, share_values = _extract_share_instance_values(
  1457. values)
  1458. _ensure_availability_zone_exists(context, share_instance_values, session,
  1459. strict=False)
  1460. with session.begin():
  1461. share_ref = share_get(context, share_id, session=session)
  1462. _share_instance_update(context, share_ref.instance['id'],
  1463. share_instance_values, session=session)
  1464. share_ref.update(share_values)
  1465. share_ref.save(session=session)
  1466. return share_ref
  1467. @require_context
  1468. def share_get(context, share_id, session=None):
  1469. result = _share_get_query(context, session).filter_by(id=share_id).first()
  1470. if result is None:
  1471. raise exception.NotFound()
  1472. return result
  1473. def _share_get_all_with_filters(context, project_id=None, share_server_id=None,
  1474. share_group_id=None, filters=None,
  1475. is_public=False, sort_key=None,
  1476. sort_dir=None):
  1477. """Returns sorted list of shares that satisfies filters.
  1478. :param context: context to query under
  1479. :param project_id: project id that owns shares
  1480. :param share_server_id: share server that hosts shares
  1481. :param filters: dict of filters to specify share selection
  1482. :param is_public: public shares from other projects will be added
  1483. to result if True
  1484. :param sort_key: key of models.Share to be used for sorting
  1485. :param sort_dir: desired direction of sorting, can be 'asc' and 'desc'
  1486. :returns: list -- models.Share
  1487. :raises: exception.InvalidInput
  1488. """
  1489. if not sort_key:
  1490. sort_key = 'created_at'
  1491. if not sort_dir:
  1492. sort_dir = 'desc'
  1493. query = (
  1494. _share_get_query(context).join(
  1495. models.ShareInstance,
  1496. models.ShareInstance.share_id == models.Share.id
  1497. )
  1498. )
  1499. if project_id:
  1500. if is_public:
  1501. query = query.filter(or_(models.Share.project_id == project_id,
  1502. models.Share.is_public))
  1503. else:
  1504. query = query.filter(models.Share.project_id == project_id)
  1505. if share_server_id:
  1506. query = query.filter(
  1507. models.ShareInstance.share_server_id == share_server_id)
  1508. if share_group_id:
  1509. query = query.filter(
  1510. models.Share.share_group_id == share_group_id)
  1511. # Apply filters
  1512. if not filters:
  1513. filters = {}
  1514. export_location_id = filters.get('export_location_id')
  1515. export_location_path = filters.get('export_location_path')
  1516. if export_location_id or export_location_path:
  1517. query = query.join(
  1518. models.ShareInstanceExportLocations,
  1519. models.ShareInstanceExportLocations.share_instance_id ==
  1520. models.ShareInstance.id)
  1521. if export_location_path:
  1522. query = query.filter(
  1523. models.ShareInstanceExportLocations.path ==
  1524. export_location_path)
  1525. if export_location_id:
  1526. query = query.filter(
  1527. models.ShareInstanceExportLocations.uuid ==
  1528. export_location_id)
  1529. if 'metadata' in filters:
  1530. for k, v in filters['metadata'].items():
  1531. # pylint: disable=no-member
  1532. query = query.filter(
  1533. or_(models.Share.share_metadata.any(
  1534. key=k, value=v)))
  1535. if 'extra_specs' in filters:
  1536. query = query.join(
  1537. models.ShareTypeExtraSpecs,
  1538. models.ShareTypeExtraSpecs.share_type_id ==
  1539. models.ShareInstance.share_type_id)
  1540. for k, v in filters['extra_specs'].items():
  1541. query = query.filter(or_(models.ShareTypeExtraSpecs.key == k,
  1542. models.ShareTypeExtraSpecs.value == v))
  1543. try:
  1544. query = apply_sorting(models.Share, query, sort_key, sort_dir)
  1545. except AttributeError:
  1546. try:
  1547. query = apply_sorting(
  1548. models.ShareInstance, query, sort_key, sort_dir)
  1549. except AttributeError:
  1550. msg = _("Wrong sorting key provided - '%s'.") % sort_key
  1551. raise exception.InvalidInput(reason=msg)
  1552. if 'limit' in filters:
  1553. offset = filters.get('offset', 0)
  1554. query = query.limit(filters['limit']).offset(offset)
  1555. # Returns list of shares that satisfy filters.
  1556. query = query.all()
  1557. return query
  1558. @require_admin_context
  1559. def share_get_all(context, filters=None, sort_key=None, sort_dir=None):
  1560. query = _share_get_all_with_filters(
  1561. context, filters=filters, sort_key=sort_key, sort_dir=sort_dir)
  1562. return query
  1563. @require_context
  1564. def share_get_all_by_project(context, project_id, filters=None,
  1565. is_public=False, sort_key=None, sort_dir=None):
  1566. """Returns list of shares with given project ID."""
  1567. query = _share_get_all_with_filters(
  1568. context, project_id=project_id, filters=filters, is_public=is_public,
  1569. sort_key=sort_key, sort_dir=sort_dir,
  1570. )
  1571. return query
  1572. @require_context
  1573. def share_get_all_by_share_group_id(context, share_group_id,
  1574. filters=None, sort_key=None,
  1575. sort_dir=None):
  1576. """Returns list of shares with given group ID."""
  1577. query = _share_get_all_with_filters(
  1578. context, share_group_id=share_group_id,
  1579. filters=filters, sort_key=sort_key, sort_dir=sort_dir,
  1580. )
  1581. return query
  1582. @require_context
  1583. def share_get_all_by_share_server(context, share_server_id, filters=None,
  1584. sort_key=None, sort_dir=None):
  1585. """Returns list of shares with given share server."""
  1586. query = _share_get_all_with_filters(
  1587. context, share_server_id=share_server_id, filters=filters,
  1588. sort_key=sort_key, sort_dir=sort_dir,
  1589. )
  1590. return query
  1591. @require_context
  1592. def share_delete(context, share_id):
  1593. session = get_session()
  1594. with session.begin():
  1595. share_ref = share_get(context, share_id, session)
  1596. if len(share_ref.instances) > 0:
  1597. msg = _("Share %(id)s has %(count)s share instances.") % {
  1598. 'id': share_id, 'count': len(share_ref.instances)}
  1599. raise exception.InvalidShare(msg)
  1600. share_ref.soft_delete(session=session)
  1601. (session.query(models.ShareMetadata).
  1602. filter_by(share_id=share_id).soft_delete())
  1603. ###################
  1604. def _share_access_get_query(context, session, values, read_deleted='no'):
  1605. """Get access record."""
  1606. query = (model_query(
  1607. context, models.ShareAccessMapping, session=session,
  1608. read_deleted=read_deleted).options(
  1609. joinedload('share_access_rules_metadata')))
  1610. return query.filter_by(**values)
  1611. def _share_instance_access_query(context, session, access_id=None,
  1612. instance_id=None):
  1613. filters = {'deleted': 'False'}
  1614. if access_id is not None:
  1615. filters.update({'access_id': access_id})
  1616. if instance_id is not None:
  1617. filters.update({'share_instance_id': instance_id})
  1618. return model_query(context, models.ShareInstanceAccessMapping,
  1619. session=session).filter_by(**filters)
  1620. def _share_access_metadata_get_item(context, access_id, key, session=None):
  1621. result = (_share_access_metadata_get_query(
  1622. context, access_id, session=session).filter_by(key=key).first())
  1623. if not result:
  1624. raise exception.ShareAccessMetadataNotFound(
  1625. metadata_key=key, access_id=access_id)
  1626. return result
  1627. def _share_access_metadata_get_query(context, access_id, session=None):
  1628. return (model_query(
  1629. context, models.ShareAccessRulesMetadata, session=session,
  1630. read_deleted="no").
  1631. filter_by(access_id=access_id).
  1632. options(joinedload('access')))
  1633. @require_context
  1634. def share_access_metadata_update(context, access_id, metadata):
  1635. session = get_session()
  1636. with session.begin():
  1637. # Now update all existing items with new values, or create new meta
  1638. # objects
  1639. for meta_key, meta_value in metadata.items():
  1640. # update the value whether it exists or not
  1641. item = {"value": meta_value}
  1642. try:
  1643. meta_ref = _share_access_metadata_get_item(
  1644. context, access_id, meta_key, session=session)
  1645. except exception.ShareAccessMetadataNotFound:
  1646. meta_ref = models.ShareAccessRulesMetadata()
  1647. item.update({"key": meta_key, "access_id": access_id})
  1648. meta_ref.update(item)
  1649. meta_ref.save(session=session)
  1650. return metadata
  1651. @require_context
  1652. def share_access_metadata_delete(context, access_id, key):
  1653. session = get_session()
  1654. with session.begin():
  1655. metadata = _share_access_metadata_get_item(
  1656. context, access_id, key, session=session)
  1657. metadata.soft_delete(session)
  1658. @require_context
  1659. def share_access_create(context, values):
  1660. values = ensure_model_dict_has_id(values)
  1661. session = get_session()
  1662. with session.begin():
  1663. values['share_access_rules_metadata'] = (
  1664. _metadata_refs(values.get('metadata'),
  1665. models.ShareAccessRulesMetadata))
  1666. access_ref = models.ShareAccessMapping()
  1667. access_ref.update(values)
  1668. access_ref.save(session=session)
  1669. parent_share = share_get(context, values['share_id'], session=session)
  1670. for instance in parent_share.instances:
  1671. vals = {
  1672. 'share_instance_id': instance['id'],
  1673. 'access_id': access_ref['id'],
  1674. }
  1675. _share_instance_access_create(vals, session)
  1676. return share_access_get(context, access_ref['id'])
  1677. @require_context
  1678. def share_instance_access_create(context, values, share_instance_id):
  1679. values = ensure_model_dict_has_id(values)
  1680. session = get_session()
  1681. with session.begin():
  1682. access_list = _share_access_get_query(
  1683. context, session, {
  1684. 'share_id': values['share_id'],
  1685. 'access_type': values['access_type'],
  1686. 'access_to': values['access_to'],
  1687. }).all()
  1688. if len(access_list) > 0:
  1689. access_ref = access_list[0]
  1690. else:
  1691. access_ref = models.ShareAccessMapping()
  1692. access_ref.update(values)
  1693. access_ref.save(session=session)
  1694. vals = {
  1695. 'share_instance_id': share_instance_id,
  1696. 'access_id': access_ref['id'],
  1697. }
  1698. _share_instance_access_create(vals, session)
  1699. return share_access_get(context, access_ref['id'])
  1700. @require_context
  1701. def share_instance_access_copy(context, share_id, instance_id, session=None):
  1702. """Copy access rules from share to share instance."""
  1703. session = session or get_session()
  1704. share_access_rules = _share_access_get_query(
  1705. context, session, {'share_id': share_id}).all()
  1706. for access_rule in share_access_rules:
  1707. values = {
  1708. 'share_instance_id': instance_id,
  1709. 'access_id': access_rule['id'],
  1710. }
  1711. _share_instance_access_create(values, session)
  1712. return share_access_rules
  1713. def _share_instance_access_create(values, session):
  1714. access_ref = models.ShareInstanceAccessMapping()
  1715. access_ref.update(ensure_model_dict_has_id(values))
  1716. access_ref.save(session=session)
  1717. return access_ref
  1718. @require_context
  1719. def share_access_get(context, access_id, session=None):
  1720. """Get access record."""
  1721. session = session or get_session()
  1722. access = _share_access_get_query(
  1723. context, session, {'id': access_id}).first()
  1724. if access:
  1725. return access
  1726. else:
  1727. raise exception.NotFound()
  1728. @require_context
  1729. def share_instance_access_get(context, access_id, instance_id,
  1730. with_share_access_data=True):
  1731. """Get access record."""
  1732. session = get_session()
  1733. access = _share_instance_access_query(context, session, access_id,
  1734. instance_id).first()
  1735. if access is None:
  1736. raise exception.NotFound()
  1737. if with_share_access_data:
  1738. access = _set_instances_share_access_data(context, access, session)[0]
  1739. return access
  1740. @require_context
  1741. def share_access_get_all_for_share(context, share_id, filters=None,
  1742. session=None):
  1743. filters = filters or {}
  1744. session = session or get_session()
  1745. query = (_share_access_get_query(
  1746. context, session, {'share_id': share_id}).filter(
  1747. models.ShareAccessMapping.instance_mappings.any()))
  1748. if 'metadata' in filters:
  1749. for k, v in filters['metadata'].items():
  1750. query = query.filter(
  1751. or_(models.ShareAccessMapping.
  1752. share_access_rules_metadata.any(key=k, value=v)))
  1753. return query.all()
  1754. @require_context
  1755. def share_access_get_all_for_instance(context, instance_id, filters=None,
  1756. with_share_access_data=True,
  1757. session=None):
  1758. """Get all access rules related to a certain share instance."""
  1759. session = session or get_session()
  1760. filters = copy.deepcopy(filters) if filters else {}
  1761. filters.update({'share_instance_id': instance_id})
  1762. legal_filter_keys = ('id', 'share_instance_id', 'access_id', 'state')
  1763. query = _share_instance_access_query(context, session)
  1764. query = exact_filter(
  1765. query, models.ShareInstanceAccessMapping, filters, legal_filter_keys)
  1766. instance_accesses = query.all()
  1767. if with_share_access_data:
  1768. instance_accesses = _set_instances_share_access_data(
  1769. context, instance_accesses, session)
  1770. return instance_accesses
  1771. def _set_instances_share_access_data(context, instance_accesses, session):
  1772. if instance_accesses and not isinstance(instance_accesses, list):
  1773. instance_accesses = [instance_accesses]
  1774. for instance_access in instance_accesses:
  1775. share_access = share_access_get(
  1776. context, instance_access['access_id'], session=session)
  1777. instance_access.set_share_access_data(share_access)
  1778. return instance_accesses
  1779. def _set_instances_snapshot_access_data(context, instance_accesses, session):
  1780. if instance_accesses and not isinstance(instance_accesses, list):
  1781. instance_accesses = [instance_accesses]
  1782. for instance_access in instance_accesses:
  1783. snapshot_access = share_snapshot_access_get(
  1784. context, instance_access['access_id'], session=session)
  1785. instance_access.set_snapshot_access_data(snapshot_access)
  1786. return instance_accesses
  1787. @require_context
  1788. def share_access_get_all_by_type_and_access(context, share_id, access_type,
  1789. access):
  1790. session = get_session()
  1791. return _share_access_get_query(context, session,
  1792. {'share_id': share_id,
  1793. 'access_type': access_type,
  1794. 'access_to': access}).all()
  1795. @require_context
  1796. def share_access_check_for_existing_access(context, share_id, access_type,
  1797. access_to):
  1798. return _check_for_existing_access(
  1799. context, 'share', share_id, access_type, access_to)
  1800. def _check_for_existing_access(context, resource, resource_id, access_type,
  1801. access_to):
  1802. session = get_session()
  1803. if resource == 'share':
  1804. query_method = _share_access_get_query
  1805. access_to_field = models.ShareAccessMapping.access_to
  1806. else:
  1807. query_method = _share_snapshot_access_get_query
  1808. access_to_field = models.ShareSnapshotAccessMapping.access_to
  1809. with session.begin():
  1810. if access_type == 'ip':
  1811. rules = query_method(
  1812. context, session, {'%s_id' % resource: resource_id,
  1813. 'access_type': access_type}).filter(
  1814. access_to_field.startswith(access_to.split('/')[0])).all()
  1815. matching_rules = [
  1816. rule for rule in rules if
  1817. ipaddress.ip_network(six.text_type(access_to)) ==
  1818. ipaddress.ip_network(six.text_type(rule['access_to']))
  1819. ]
  1820. return len(matching_rules) > 0
  1821. else:
  1822. return query_method(
  1823. context, session, {'%s_id' % resource: resource_id,
  1824. 'access_type': access_type,
  1825. 'access_to': access_to}).count() > 0
  1826. @require_context
  1827. def share_access_delete_all_by_share(context, share_id):
  1828. session = get_session()
  1829. with session.begin():
  1830. (session.query(models.ShareAccessMapping).
  1831. filter_by(share_id=share_id).soft_delete())
  1832. @require_context
  1833. def share_instance_access_delete(context, mapping_id):
  1834. session = get_session()
  1835. with session.begin():
  1836. mapping = (session.query(models.ShareInstanceAccessMapping).
  1837. filter_by(id=mapping_id).first())
  1838. if not mapping:
  1839. exception.NotFound()
  1840. mapping.soft_delete(session, update_status=True,
  1841. status_field_name='state')
  1842. other_mappings = _share_instance_access_query(
  1843. context, session, mapping['access_id']).all()
  1844. # NOTE(u_glide): Remove access rule if all mappings were removed.
  1845. if len(other_mappings) == 0:
  1846. (session.query(models.ShareAccessRulesMetadata).filter_by(
  1847. access_id=mapping['access_id']).soft_delete())
  1848. (session.query(models.ShareAccessMapping).filter_by(
  1849. id=mapping['access_id']).soft_delete())
  1850. @require_context
  1851. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  1852. def share_instance_access_update(context, access_id, instance_id, updates):
  1853. session = get_session()
  1854. share_access_fields = ('access_type', 'access_to', 'access_key',
  1855. 'access_level')
  1856. share_access_map_updates, share_instance_access_map_updates = (
  1857. _extract_subdict_by_fields(updates, share_access_fields)
  1858. )
  1859. with session.begin():
  1860. share_access = _share_access_get_query(
  1861. context, session, {'id': access_id}).first()
  1862. share_access.update(share_access_map_updates)
  1863. share_access.save(session=session)
  1864. access = _share_instance_access_query(
  1865. context, session, access_id, instance_id).first()
  1866. access.update(share_instance_access_map_updates)
  1867. access.save(session=session)
  1868. return access
  1869. ###################
  1870. @require_context
  1871. def share_snapshot_instance_create(context, snapshot_id, values, session=None):
  1872. session = session or get_session()
  1873. values = copy.deepcopy(values)
  1874. _change_size_to_instance_size(values)
  1875. if not values.get('id'):
  1876. values['id'] = uuidutils.generate_uuid()
  1877. values.update({'snapshot_id': snapshot_id})
  1878. instance_ref = models.ShareSnapshotInstance()
  1879. instance_ref.update(values)
  1880. instance_ref.save(session=session)
  1881. return share_snapshot_instance_get(context, instance_ref['id'],
  1882. session=session)
  1883. @require_context
  1884. def share_snapshot_instance_update(context, instance_id, values):
  1885. session = get_session()
  1886. instance_ref = share_snapshot_instance_get(context, instance_id,
  1887. session=session)
  1888. _change_size_to_instance_size(values)
  1889. # NOTE(u_glide): Ignore updates to custom properties
  1890. for extra_key in models.ShareSnapshotInstance._extra_keys:
  1891. if extra_key in values:
  1892. values.pop(extra_key)
  1893. instance_ref.update(values)
  1894. instance_ref.save(session=session)
  1895. return instance_ref
  1896. @require_context
  1897. def share_snapshot_instance_delete(context, snapshot_instance_id,
  1898. session=None):
  1899. session = session or get_session()
  1900. with session.begin():
  1901. snapshot_instance_ref = share_snapshot_instance_get(
  1902. context, snapshot_instance_id, session=session)
  1903. access_rules = share_snapshot_access_get_all_for_snapshot_instance(
  1904. context, snapshot_instance_id, session=session)
  1905. for rule in access_rules:
  1906. share_snapshot_instance_access_delete(
  1907. context, rule['access_id'], snapshot_instance_id)
  1908. for el in snapshot_instance_ref.export_locations:
  1909. share_snapshot_instance_export_location_delete(context, el['id'])
  1910. snapshot_instance_ref.soft_delete(
  1911. session=session, update_status=True)
  1912. snapshot = share_snapshot_get(
  1913. context, snapshot_instance_ref['snapshot_id'], session=session)
  1914. if len(snapshot.instances) == 0:
  1915. snapshot.soft_delete(session=session)
  1916. @require_context
  1917. def share_snapshot_instance_get(context, snapshot_instance_id, session=None,
  1918. with_share_data=False):
  1919. session = session or get_session()
  1920. result = _share_snapshot_instance_get_with_filters(
  1921. context, instance_ids=[snapshot_instance_id], session=session).first()
  1922. if result is None:
  1923. raise exception.ShareSnapshotInstanceNotFound(
  1924. instance_id=snapshot_instance_id)
  1925. if with_share_data:
  1926. result = _set_share_snapshot_instance_data(context, result, session)[0]
  1927. return result
  1928. @require_context
  1929. def share_snapshot_instance_get_all_with_filters(context, search_filters,
  1930. with_share_data=False,
  1931. session=None):
  1932. """Get snapshot instances filtered by known attrs, ignore unknown attrs.
  1933. All filters accept list/tuples to filter on, along with simple values.
  1934. """
  1935. def listify(values):
  1936. if values:
  1937. if not isinstance(values, (list, tuple, set)):
  1938. return values,
  1939. else:
  1940. return values
  1941. session = session or get_session()
  1942. _known_filters = ('instance_ids', 'snapshot_ids', 'share_instance_ids',
  1943. 'statuses')
  1944. filters = {k: listify(search_filters.get(k)) for k in _known_filters}
  1945. result = _share_snapshot_instance_get_with_filters(
  1946. context, session=session, **filters).all()
  1947. if with_share_data:
  1948. result = _set_share_snapshot_instance_data(context, result, session)
  1949. return result
  1950. def _share_snapshot_instance_get_with_filters(context, instance_ids=None,
  1951. snapshot_ids=None, statuses=None,
  1952. share_instance_ids=None,
  1953. session=None):
  1954. query = model_query(context, models.ShareSnapshotInstance, session=session,
  1955. read_deleted="no")
  1956. if instance_ids is not None:
  1957. query = query.filter(
  1958. models.ShareSnapshotInstance.id.in_(instance_ids))
  1959. if snapshot_ids is not None:
  1960. query = query.filter(
  1961. models.ShareSnapshotInstance.snapshot_id.in_(snapshot_ids))
  1962. if share_instance_ids is not None:
  1963. query = query.filter(models.ShareSnapshotInstance.share_instance_id
  1964. .in_(share_instance_ids))
  1965. if statuses is not None:
  1966. query = query.filter(models.ShareSnapshotInstance.status.in_(statuses))
  1967. query = query.options(joinedload('share_group_snapshot'))
  1968. return query
  1969. def _set_share_snapshot_instance_data(context, snapshot_instances, session):
  1970. if snapshot_instances and not isinstance(snapshot_instances, list):
  1971. snapshot_instances = [snapshot_instances]
  1972. for snapshot_instance in snapshot_instances:
  1973. share_instance = share_instance_get(
  1974. context, snapshot_instance['share_instance_id'], session=session,
  1975. with_share_data=True)
  1976. snapshot_instance['share'] = share_instance
  1977. return snapshot_instances
  1978. ###################
  1979. @require_context
  1980. def share_snapshot_create(context, create_values,
  1981. create_snapshot_instance=True):
  1982. values = copy.deepcopy(create_values)
  1983. values = ensure_model_dict_has_id(values)
  1984. snapshot_ref = models.ShareSnapshot()
  1985. snapshot_instance_values, snapshot_values = (
  1986. _extract_snapshot_instance_values(values)
  1987. )
  1988. share_ref = share_get(context, snapshot_values.get('share_id'))
  1989. snapshot_instance_values.update(
  1990. {'share_instance_id': share_ref.instance.id}
  1991. )
  1992. snapshot_ref.update(snapshot_values)
  1993. session = get_session()
  1994. with session.begin():
  1995. snapshot_ref.save(session=session)
  1996. if create_snapshot_instance:
  1997. share_snapshot_instance_create(
  1998. context,
  1999. snapshot_ref['id'],
  2000. snapshot_instance_values,
  2001. session=session
  2002. )
  2003. return share_snapshot_get(
  2004. context, snapshot_values['id'], session=session)
  2005. @require_admin_context
  2006. def snapshot_data_get_for_project(context, project_id, user_id,
  2007. share_type_id=None, session=None):
  2008. query = (model_query(context, models.ShareSnapshot,
  2009. func.count(models.ShareSnapshot.id),
  2010. func.sum(models.ShareSnapshot.size),
  2011. read_deleted="no",
  2012. session=session).
  2013. filter_by(project_id=project_id))
  2014. if share_type_id:
  2015. query = query.join(
  2016. models.ShareInstance,
  2017. models.ShareInstance.share_id == models.ShareSnapshot.share_id,
  2018. ).filter_by(share_type_id=share_type_id)
  2019. elif user_id:
  2020. query = query.filter_by(user_id=user_id)
  2021. result = query.first()
  2022. return (result[0] or 0, result[1] or 0)
  2023. @require_context
  2024. def share_snapshot_get(context, snapshot_id, session=None):
  2025. result = (model_query(context, models.ShareSnapshot, session=session,
  2026. project_only=True).
  2027. filter_by(id=snapshot_id).
  2028. options(joinedload('share')).
  2029. options(joinedload('instances')).
  2030. first())
  2031. if not result:
  2032. raise exception.ShareSnapshotNotFound(snapshot_id=snapshot_id)
  2033. return result
  2034. def _share_snapshot_get_all_with_filters(context, project_id=None,
  2035. share_id=None, filters=None,
  2036. sort_key=None, sort_dir=None):
  2037. # Init data
  2038. sort_key = sort_key or 'share_id'
  2039. sort_dir = sort_dir or 'desc'
  2040. filters = filters or {}
  2041. query = model_query(context, models.ShareSnapshot)
  2042. if project_id:
  2043. query = query.filter_by(project_id=project_id)
  2044. if share_id:
  2045. query = query.filter_by(share_id=share_id)
  2046. query = query.options(joinedload('share'))
  2047. query = query.options(joinedload('instances'))
  2048. # Apply filters
  2049. if 'usage' in filters:
  2050. usage_filter_keys = ['any', 'used', 'unused']
  2051. if filters['usage'] == 'any':
  2052. pass
  2053. elif filters['usage'] == 'used':
  2054. query = query.filter(or_(models.Share.snapshot_id == (
  2055. models.ShareSnapshot.id)))
  2056. elif filters['usage'] == 'unused':
  2057. query = query.filter(or_(models.Share.snapshot_id != (
  2058. models.ShareSnapshot.id)))
  2059. else:
  2060. msg = _("Wrong 'usage' key provided - '%(key)s'. "
  2061. "Expected keys are '%(ek)s'.") % {
  2062. 'key': filters['usage'],
  2063. 'ek': six.text_type(usage_filter_keys)}
  2064. raise exception.InvalidInput(reason=msg)
  2065. # Apply sorting
  2066. try:
  2067. attr = getattr(models.ShareSnapshot, sort_key)
  2068. except AttributeError:
  2069. msg = _("Wrong sorting key provided - '%s'.") % sort_key
  2070. raise exception.InvalidInput(reason=msg)
  2071. if sort_dir.lower() == 'desc':
  2072. query = query.order_by(attr.desc())
  2073. elif sort_dir.lower() == 'asc':
  2074. query = query.order_by(attr.asc())
  2075. else:
  2076. msg = _("Wrong sorting data provided: sort key is '%(sort_key)s' "
  2077. "and sort direction is '%(sort_dir)s'.") % {
  2078. "sort_key": sort_key, "sort_dir": sort_dir}
  2079. raise exception.InvalidInput(reason=msg)
  2080. # Returns list of shares that satisfy filters
  2081. return query.all()
  2082. @require_admin_context
  2083. def share_snapshot_get_all(context, filters=None, sort_key=None,
  2084. sort_dir=None):
  2085. return _share_snapshot_get_all_with_filters(
  2086. context, filters=filters, sort_key=sort_key, sort_dir=sort_dir,
  2087. )
  2088. @require_context
  2089. def share_snapshot_get_all_by_project(context, project_id, filters=None,
  2090. sort_key=None, sort_dir=None):
  2091. authorize_project_context(context, project_id)
  2092. return _share_snapshot_get_all_with_filters(
  2093. context, project_id=project_id,
  2094. filters=filters, sort_key=sort_key, sort_dir=sort_dir,
  2095. )
  2096. @require_context
  2097. def share_snapshot_get_all_for_share(context, share_id, filters=None,
  2098. sort_key=None, sort_dir=None):
  2099. return _share_snapshot_get_all_with_filters(
  2100. context, share_id=share_id,
  2101. filters=filters, sort_key=sort_key, sort_dir=sort_dir,
  2102. )
  2103. @require_context
  2104. def share_snapshot_get_latest_for_share(context, share_id):
  2105. snapshots = _share_snapshot_get_all_with_filters(
  2106. context, share_id=share_id, sort_key='created_at', sort_dir='desc')
  2107. return snapshots[0] if snapshots else None
  2108. @require_context
  2109. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  2110. def share_snapshot_update(context, snapshot_id, values):
  2111. session = get_session()
  2112. with session.begin():
  2113. snapshot_ref = share_snapshot_get(context, snapshot_id,
  2114. session=session)
  2115. instance_values, snapshot_values = (
  2116. _extract_snapshot_instance_values(values)
  2117. )
  2118. if snapshot_values:
  2119. snapshot_ref.update(snapshot_values)
  2120. snapshot_ref.save(session=session)
  2121. if instance_values:
  2122. snapshot_ref.instance.update(instance_values)
  2123. snapshot_ref.instance.save(session=session)
  2124. return snapshot_ref
  2125. #################################
  2126. @require_context
  2127. def share_snapshot_access_create(context, values):
  2128. values = ensure_model_dict_has_id(values)
  2129. session = get_session()
  2130. with session.begin():
  2131. access_ref = models.ShareSnapshotAccessMapping()
  2132. access_ref.update(values)
  2133. access_ref.save(session=session)
  2134. snapshot = share_snapshot_get(context, values['share_snapshot_id'],
  2135. session=session)
  2136. for instance in snapshot.instances:
  2137. vals = {
  2138. 'share_snapshot_instance_id': instance['id'],
  2139. 'access_id': access_ref['id'],
  2140. }
  2141. _share_snapshot_instance_access_create(vals, session)
  2142. return share_snapshot_access_get(context, access_ref['id'])
  2143. def _share_snapshot_access_get_query(context, session, filters,
  2144. read_deleted='no'):
  2145. query = model_query(context, models.ShareSnapshotAccessMapping,
  2146. session=session, read_deleted=read_deleted)
  2147. return query.filter_by(**filters)
  2148. def _share_snapshot_instance_access_get_query(context, session,
  2149. access_id=None,
  2150. share_snapshot_instance_id=None):
  2151. filters = {'deleted': 'False'}
  2152. if access_id is not None:
  2153. filters.update({'access_id': access_id})
  2154. if share_snapshot_instance_id is not None:
  2155. filters.update(
  2156. {'share_snapshot_instance_id': share_snapshot_instance_id})
  2157. return model_query(context, models.ShareSnapshotInstanceAccessMapping,
  2158. session=session).filter_by(**filters)
  2159. @require_context
  2160. def share_snapshot_instance_access_get_all(context, access_id, session):
  2161. rules = _share_snapshot_instance_access_get_query(
  2162. context, session, access_id=access_id).all()
  2163. return rules
  2164. @require_context
  2165. def share_snapshot_access_get(context, access_id, session=None):
  2166. session = session or get_session()
  2167. access = _share_snapshot_access_get_query(
  2168. context, session, {'id': access_id}).first()
  2169. if access:
  2170. return access
  2171. else:
  2172. raise exception.NotFound()
  2173. def _share_snapshot_instance_access_create(values, session):
  2174. access_ref = models.ShareSnapshotInstanceAccessMapping()
  2175. access_ref.update(ensure_model_dict_has_id(values))
  2176. access_ref.save(session=session)
  2177. return access_ref
  2178. @require_context
  2179. def share_snapshot_access_get_all_for_share_snapshot(context,
  2180. share_snapshot_id,
  2181. filters):
  2182. session = get_session()
  2183. filters['share_snapshot_id'] = share_snapshot_id
  2184. access_list = _share_snapshot_access_get_query(
  2185. context, session, filters).all()
  2186. return access_list
  2187. @require_context
  2188. def share_snapshot_check_for_existing_access(context, share_snapshot_id,
  2189. access_type, access_to):
  2190. return _check_for_existing_access(
  2191. context, 'share_snapshot', share_snapshot_id, access_type, access_to)
  2192. @require_context
  2193. def share_snapshot_access_get_all_for_snapshot_instance(
  2194. context, snapshot_instance_id, filters=None,
  2195. with_snapshot_access_data=True, session=None):
  2196. """Get all access rules related to a certain snapshot instance."""
  2197. session = session or get_session()
  2198. filters = copy.deepcopy(filters) if filters else {}
  2199. filters.update({'share_snapshot_instance_id': snapshot_instance_id})
  2200. query = _share_snapshot_instance_access_get_query(context, session)
  2201. legal_filter_keys = (
  2202. 'id', 'share_snapshot_instance_id', 'access_id', 'state')
  2203. query = exact_filter(
  2204. query, models.ShareSnapshotInstanceAccessMapping, filters,
  2205. legal_filter_keys)
  2206. instance_accesses = query.all()
  2207. if with_snapshot_access_data:
  2208. instance_accesses = _set_instances_snapshot_access_data(
  2209. context, instance_accesses, session)
  2210. return instance_accesses
  2211. @require_context
  2212. def share_snapshot_instance_access_update(
  2213. context, access_id, instance_id, updates):
  2214. snapshot_access_fields = ('access_type', 'access_to')
  2215. snapshot_access_map_updates, share_instance_access_map_updates = (
  2216. _extract_subdict_by_fields(updates, snapshot_access_fields)
  2217. )
  2218. session = get_session()
  2219. with session.begin():
  2220. snapshot_access = _share_snapshot_access_get_query(
  2221. context, session, {'id': access_id}).first()
  2222. if not snapshot_access:
  2223. raise exception.NotFound()
  2224. snapshot_access.update(snapshot_access_map_updates)
  2225. snapshot_access.save(session=session)
  2226. access = _share_snapshot_instance_access_get_query(
  2227. context, session, access_id=access_id,
  2228. share_snapshot_instance_id=instance_id).first()
  2229. if not access:
  2230. raise exception.NotFound()
  2231. access.update(share_instance_access_map_updates)
  2232. access.save(session=session)
  2233. return access
  2234. @require_context
  2235. def share_snapshot_instance_access_get(
  2236. context, access_id, share_snapshot_instance_id,
  2237. with_snapshot_access_data=True):
  2238. session = get_session()
  2239. with session.begin():
  2240. access = _share_snapshot_instance_access_get_query(
  2241. context, session, access_id=access_id,
  2242. share_snapshot_instance_id=share_snapshot_instance_id).first()
  2243. if access is None:
  2244. raise exception.NotFound()
  2245. if with_snapshot_access_data:
  2246. return _set_instances_snapshot_access_data(
  2247. context, access, session)[0]
  2248. else:
  2249. return access
  2250. @require_context
  2251. def share_snapshot_instance_access_delete(
  2252. context, access_id, snapshot_instance_id):
  2253. session = get_session()
  2254. with session.begin():
  2255. rule = _share_snapshot_instance_access_get_query(
  2256. context, session, access_id=access_id,
  2257. share_snapshot_instance_id=snapshot_instance_id).first()
  2258. if not rule:
  2259. exception.NotFound()
  2260. rule.soft_delete(session, update_status=True,
  2261. status_field_name='state')
  2262. other_mappings = share_snapshot_instance_access_get_all(
  2263. context, rule['access_id'], session)
  2264. if len(other_mappings) == 0:
  2265. (
  2266. session.query(models.ShareSnapshotAccessMapping)
  2267. .filter_by(id=rule['access_id'])
  2268. .soft_delete(update_status=True, status_field_name='state')
  2269. )
  2270. @require_context
  2271. def share_snapshot_instance_export_location_create(context, values):
  2272. values = ensure_model_dict_has_id(values)
  2273. session = get_session()
  2274. with session.begin():
  2275. ssiel = models.ShareSnapshotInstanceExportLocation()
  2276. ssiel.update(values)
  2277. ssiel.save(session=session)
  2278. return ssiel
  2279. def _share_snapshot_instance_export_locations_get_query(context, session,
  2280. values):
  2281. query = model_query(context, models.ShareSnapshotInstanceExportLocation,
  2282. session=session)
  2283. return query.filter_by(**values)
  2284. @require_context
  2285. def share_snapshot_export_locations_get(context, snapshot_id):
  2286. session = get_session()
  2287. snapshot = share_snapshot_get(context, snapshot_id, session=session)
  2288. ins_ids = [ins['id'] for ins in snapshot.instances]
  2289. export_locations = _share_snapshot_instance_export_locations_get_query(
  2290. context, session, {}).filter(
  2291. models.ShareSnapshotInstanceExportLocation.
  2292. share_snapshot_instance_id.in_(ins_ids)).all()
  2293. return export_locations
  2294. @require_context
  2295. def share_snapshot_instance_export_locations_get_all(
  2296. context, share_snapshot_instance_id):
  2297. session = get_session()
  2298. export_locations = _share_snapshot_instance_export_locations_get_query(
  2299. context, session,
  2300. {'share_snapshot_instance_id': share_snapshot_instance_id}).all()
  2301. return export_locations
  2302. @require_context
  2303. def share_snapshot_instance_export_location_get(context, el_id):
  2304. session = get_session()
  2305. export_location = _share_snapshot_instance_export_locations_get_query(
  2306. context, session, {'id': el_id}).first()
  2307. if export_location:
  2308. return export_location
  2309. else:
  2310. raise exception.NotFound()
  2311. @require_context
  2312. def share_snapshot_instance_export_location_delete(context, el_id):
  2313. session = get_session()
  2314. with session.begin():
  2315. el = _share_snapshot_instance_export_locations_get_query(
  2316. context, session, {'id': el_id}).first()
  2317. if not el:
  2318. exception.NotFound()
  2319. el.soft_delete(session=session)
  2320. #################################
  2321. @require_context
  2322. @require_share_exists
  2323. def share_metadata_get(context, share_id):
  2324. return _share_metadata_get(context, share_id)
  2325. @require_context
  2326. @require_share_exists
  2327. def share_metadata_delete(context, share_id, key):
  2328. (_share_metadata_get_query(context, share_id).
  2329. filter_by(key=key).soft_delete())
  2330. @require_context
  2331. @require_share_exists
  2332. def share_metadata_update(context, share_id, metadata, delete):
  2333. return _share_metadata_update(context, share_id, metadata, delete)
  2334. def _share_metadata_get_query(context, share_id, session=None):
  2335. return (model_query(context, models.ShareMetadata, session=session,
  2336. read_deleted="no").
  2337. filter_by(share_id=share_id).
  2338. options(joinedload('share')))
  2339. def _share_metadata_get(context, share_id, session=None):
  2340. rows = _share_metadata_get_query(context, share_id,
  2341. session=session).all()
  2342. result = {}
  2343. for row in rows:
  2344. result[row['key']] = row['value']
  2345. return result
  2346. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  2347. def _share_metadata_update(context, share_id, metadata, delete, session=None):
  2348. if not session:
  2349. session = get_session()
  2350. with session.begin():
  2351. # Set existing metadata to deleted if delete argument is True
  2352. if delete:
  2353. original_metadata = _share_metadata_get(context, share_id,
  2354. session=session)
  2355. for meta_key, meta_value in original_metadata.items():
  2356. if meta_key not in metadata:
  2357. meta_ref = _share_metadata_get_item(context, share_id,
  2358. meta_key,
  2359. session=session)
  2360. meta_ref.soft_delete(session=session)
  2361. meta_ref = None
  2362. # Now update all existing items with new values, or create new meta
  2363. # objects
  2364. for meta_key, meta_value in metadata.items():
  2365. # update the value whether it exists or not
  2366. item = {"value": meta_value}
  2367. try:
  2368. meta_ref = _share_metadata_get_item(context, share_id,
  2369. meta_key,
  2370. session=session)
  2371. except exception.ShareMetadataNotFound:
  2372. meta_ref = models.ShareMetadata()
  2373. item.update({"key": meta_key, "share_id": share_id})
  2374. meta_ref.update(item)
  2375. meta_ref.save(session=session)
  2376. return metadata
  2377. def _share_metadata_get_item(context, share_id, key, session=None):
  2378. result = (_share_metadata_get_query(context, share_id, session=session).
  2379. filter_by(key=key).
  2380. first())
  2381. if not result:
  2382. raise exception.ShareMetadataNotFound(metadata_key=key,
  2383. share_id=share_id)
  2384. return result
  2385. ############################
  2386. # Export locations functions
  2387. ############################
  2388. def _share_export_locations_get(context, share_instance_ids,
  2389. include_admin_only=True,
  2390. ignore_secondary_replicas=False, session=None):
  2391. session = session or get_session()
  2392. if not isinstance(share_instance_ids, (set, list, tuple)):
  2393. share_instance_ids = (share_instance_ids, )
  2394. query = model_query(
  2395. context,
  2396. models.ShareInstanceExportLocations,
  2397. session=session,
  2398. read_deleted="no",
  2399. ).filter(
  2400. models.ShareInstanceExportLocations.share_instance_id.in_(
  2401. share_instance_ids),
  2402. ).order_by(
  2403. "updated_at",
  2404. ).options(
  2405. joinedload("_el_metadata_bare"),
  2406. )
  2407. if not include_admin_only:
  2408. query = query.filter_by(is_admin_only=False)
  2409. if ignore_secondary_replicas:
  2410. replica_state_attr = models.ShareInstance.replica_state
  2411. query = query.join("share_instance").filter(
  2412. or_(replica_state_attr == None, # noqa
  2413. replica_state_attr == constants.REPLICA_STATE_ACTIVE))
  2414. return query.all()
  2415. @require_context
  2416. @require_share_exists
  2417. def share_export_locations_get_by_share_id(context, share_id,
  2418. include_admin_only=True,
  2419. ignore_migration_destination=False,
  2420. ignore_secondary_replicas=False):
  2421. share = share_get(context, share_id)
  2422. if ignore_migration_destination:
  2423. ids = [instance.id for instance in share.instances
  2424. if instance['status'] != constants.STATUS_MIGRATING_TO]
  2425. else:
  2426. ids = [instance.id for instance in share.instances]
  2427. rows = _share_export_locations_get(
  2428. context, ids, include_admin_only=include_admin_only,
  2429. ignore_secondary_replicas=ignore_secondary_replicas)
  2430. return rows
  2431. @require_context
  2432. @require_share_instance_exists
  2433. def share_export_locations_get_by_share_instance_id(context,
  2434. share_instance_id,
  2435. include_admin_only=True):
  2436. rows = _share_export_locations_get(
  2437. context, [share_instance_id], include_admin_only=include_admin_only)
  2438. return rows
  2439. @require_context
  2440. @require_share_exists
  2441. def share_export_locations_get(context, share_id):
  2442. # NOTE(vponomaryov): this method is kept for compatibility with
  2443. # old approach. New one uses 'share_export_locations_get_by_share_id'.
  2444. # Which returns list of dicts instead of list of strings, as this one does.
  2445. share = share_get(context, share_id)
  2446. rows = _share_export_locations_get(
  2447. context, share.instance.id, context.is_admin)
  2448. return [location['path'] for location in rows]
  2449. @require_context
  2450. def share_export_location_get_by_uuid(context, export_location_uuid,
  2451. ignore_secondary_replicas=False,
  2452. session=None):
  2453. session = session or get_session()
  2454. query = model_query(
  2455. context,
  2456. models.ShareInstanceExportLocations,
  2457. session=session,
  2458. read_deleted="no",
  2459. ).filter_by(
  2460. uuid=export_location_uuid,
  2461. ).options(
  2462. joinedload("_el_metadata_bare"),
  2463. )
  2464. if ignore_secondary_replicas:
  2465. replica_state_attr = models.ShareInstance.replica_state
  2466. query = query.join("share_instance").filter(
  2467. or_(replica_state_attr == None, # noqa
  2468. replica_state_attr == constants.REPLICA_STATE_ACTIVE))
  2469. result = query.first()
  2470. if not result:
  2471. raise exception.ExportLocationNotFound(uuid=export_location_uuid)
  2472. return result
  2473. @require_context
  2474. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  2475. def share_export_locations_update(context, share_instance_id, export_locations,
  2476. delete):
  2477. # NOTE(u_glide):
  2478. # Backward compatibility code for drivers,
  2479. # which return single export_location as string
  2480. if not isinstance(export_locations, (list, tuple, set)):
  2481. export_locations = (export_locations, )
  2482. export_locations_as_dicts = []
  2483. for el in export_locations:
  2484. # NOTE(vponomaryov): transform old export locations view to new one
  2485. export_location = el
  2486. if isinstance(el, six.string_types):
  2487. export_location = {
  2488. "path": el,
  2489. "is_admin_only": False,
  2490. "metadata": {},
  2491. }
  2492. elif isinstance(export_location, dict):
  2493. if 'metadata' not in export_location:
  2494. export_location['metadata'] = {}
  2495. else:
  2496. raise exception.ManilaException(
  2497. _("Wrong export location type '%s'.") % type(export_location))
  2498. export_locations_as_dicts.append(export_location)
  2499. export_locations = export_locations_as_dicts
  2500. export_locations_paths = [el['path'] for el in export_locations]
  2501. session = get_session()
  2502. current_el_rows = _share_export_locations_get(
  2503. context, share_instance_id, session=session)
  2504. def get_path_list_from_rows(rows):
  2505. return set([l['path'] for l in rows])
  2506. current_el_paths = get_path_list_from_rows(current_el_rows)
  2507. def create_indexed_time_dict(key_list):
  2508. base = timeutils.utcnow()
  2509. return {
  2510. # NOTE(u_glide): Incrementing timestamp by microseconds to make
  2511. # timestamp order match index order.
  2512. key: base + datetime.timedelta(microseconds=index)
  2513. for index, key in enumerate(key_list)
  2514. }
  2515. indexed_update_time = create_indexed_time_dict(export_locations_paths)
  2516. for el in current_el_rows:
  2517. if delete and el['path'] not in export_locations_paths:
  2518. export_location_metadata_delete(context, el['uuid'])
  2519. el.soft_delete(session)
  2520. else:
  2521. updated_at = indexed_update_time[el['path']]
  2522. el.update({
  2523. 'updated_at': updated_at,
  2524. 'deleted': 0,
  2525. })
  2526. el.save(session=session)
  2527. if el['el_metadata']:
  2528. export_location_metadata_update(
  2529. context, el['uuid'], el['el_metadata'], session=session)
  2530. # Now add new export locations
  2531. for el in export_locations:
  2532. if el['path'] in current_el_paths:
  2533. # Already updated
  2534. continue
  2535. location_ref = models.ShareInstanceExportLocations()
  2536. location_ref.update({
  2537. 'uuid': uuidutils.generate_uuid(),
  2538. 'path': el['path'],
  2539. 'share_instance_id': share_instance_id,
  2540. 'updated_at': indexed_update_time[el['path']],
  2541. 'deleted': 0,
  2542. 'is_admin_only': el.get('is_admin_only', False),
  2543. })
  2544. location_ref.save(session=session)
  2545. if not el.get('metadata'):
  2546. continue
  2547. export_location_metadata_update(
  2548. context, location_ref['uuid'], el.get('metadata'), session=session)
  2549. return get_path_list_from_rows(_share_export_locations_get(
  2550. context, share_instance_id, session=session))
  2551. #####################################
  2552. # Export locations metadata functions
  2553. #####################################
  2554. def _export_location_metadata_get_query(context, export_location_uuid,
  2555. session=None):
  2556. session = session or get_session()
  2557. export_location_id = share_export_location_get_by_uuid(
  2558. context, export_location_uuid).id
  2559. return model_query(
  2560. context, models.ShareInstanceExportLocationsMetadata, session=session,
  2561. read_deleted="no",
  2562. ).filter_by(
  2563. export_location_id=export_location_id,
  2564. )
  2565. @require_context
  2566. def export_location_metadata_get(context, export_location_uuid, session=None):
  2567. rows = _export_location_metadata_get_query(
  2568. context, export_location_uuid, session=session).all()
  2569. result = {}
  2570. for row in rows:
  2571. result[row["key"]] = row["value"]
  2572. return result
  2573. @require_context
  2574. def export_location_metadata_delete(context, export_location_uuid, keys=None):
  2575. session = get_session()
  2576. metadata = _export_location_metadata_get_query(
  2577. context, export_location_uuid, session=session,
  2578. )
  2579. # NOTE(vponomaryov): if keys is None then we delete all metadata.
  2580. if keys is not None:
  2581. keys = keys if isinstance(keys, (list, set, tuple)) else (keys, )
  2582. metadata = metadata.filter(
  2583. models.ShareInstanceExportLocationsMetadata.key.in_(keys))
  2584. metadata = metadata.all()
  2585. for meta_ref in metadata:
  2586. meta_ref.soft_delete(session=session)
  2587. @require_context
  2588. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  2589. def export_location_metadata_update(context, export_location_uuid, metadata,
  2590. delete=False, session=None):
  2591. session = session or get_session()
  2592. if delete:
  2593. original_metadata = export_location_metadata_get(
  2594. context, export_location_uuid, session=session)
  2595. keys_for_deletion = set(original_metadata).difference(metadata)
  2596. if keys_for_deletion:
  2597. export_location_metadata_delete(
  2598. context, export_location_uuid, keys=keys_for_deletion)
  2599. el = share_export_location_get_by_uuid(context, export_location_uuid)
  2600. for meta_key, meta_value in metadata.items():
  2601. # NOTE(vponomaryov): we should use separate session
  2602. # for each meta_ref because of autoincrement of integer primary key
  2603. # that will not take effect using one session and we will rewrite,
  2604. # in that case, single record - first one added with this call.
  2605. session = get_session()
  2606. if meta_value is None:
  2607. LOG.warning("%s should be properly defined in the driver.",
  2608. meta_key)
  2609. item = {"value": meta_value, "updated_at": timeutils.utcnow()}
  2610. meta_ref = _export_location_metadata_get_query(
  2611. context, export_location_uuid, session=session,
  2612. ).filter_by(
  2613. key=meta_key,
  2614. ).first()
  2615. if not meta_ref:
  2616. meta_ref = models.ShareInstanceExportLocationsMetadata()
  2617. item.update({
  2618. "key": meta_key,
  2619. "export_location_id": el.id,
  2620. })
  2621. meta_ref.update(item)
  2622. meta_ref.save(session=session)
  2623. return metadata
  2624. ###################################
  2625. @require_context
  2626. def security_service_create(context, values):
  2627. values = ensure_model_dict_has_id(values)
  2628. security_service_ref = models.SecurityService()
  2629. security_service_ref.update(values)
  2630. session = get_session()
  2631. with session.begin():
  2632. security_service_ref.save(session=session)
  2633. return security_service_ref
  2634. @require_context
  2635. def security_service_delete(context, id):
  2636. session = get_session()
  2637. with session.begin():
  2638. security_service_ref = security_service_get(context,
  2639. id,
  2640. session=session)
  2641. security_service_ref.soft_delete(session)
  2642. @require_context
  2643. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  2644. def security_service_update(context, id, values):
  2645. session = get_session()
  2646. with session.begin():
  2647. security_service_ref = security_service_get(context,
  2648. id,
  2649. session=session)
  2650. security_service_ref.update(values)
  2651. security_service_ref.save(session=session)
  2652. return security_service_ref
  2653. @require_context
  2654. def security_service_get(context, id, session=None):
  2655. result = (_security_service_get_query(context, session=session).
  2656. filter_by(id=id).first())
  2657. if result is None:
  2658. raise exception.SecurityServiceNotFound(security_service_id=id)
  2659. return result
  2660. @require_context
  2661. def security_service_get_all(context):
  2662. return _security_service_get_query(context).all()
  2663. @require_context
  2664. def security_service_get_all_by_project(context, project_id):
  2665. return (_security_service_get_query(context).
  2666. filter_by(project_id=project_id).all())
  2667. def _security_service_get_query(context, session=None):
  2668. if session is None:
  2669. session = get_session()
  2670. return model_query(context, models.SecurityService, session=session)
  2671. ###################
  2672. def _network_get_query(context, session=None):
  2673. if session is None:
  2674. session = get_session()
  2675. return (model_query(context, models.ShareNetwork, session=session,
  2676. project_only=True).
  2677. options(joinedload('share_instances'),
  2678. joinedload('security_services'),
  2679. subqueryload('share_network_subnets')))
  2680. @require_context
  2681. def share_network_create(context, values):
  2682. values = ensure_model_dict_has_id(values)
  2683. network_ref = models.ShareNetwork()
  2684. network_ref.update(values)
  2685. session = get_session()
  2686. with session.begin():
  2687. network_ref.save(session=session)
  2688. return share_network_get(context, values['id'], session)
  2689. @require_context
  2690. def share_network_delete(context, id):
  2691. session = get_session()
  2692. with session.begin():
  2693. network_ref = share_network_get(context, id, session=session)
  2694. network_ref.soft_delete(session)
  2695. @require_context
  2696. def share_network_update(context, id, values):
  2697. session = get_session()
  2698. with session.begin():
  2699. network_ref = share_network_get(context, id, session=session)
  2700. network_ref.update(values)
  2701. network_ref.save(session=session)
  2702. return network_ref
  2703. @require_context
  2704. def share_network_get(context, id, session=None):
  2705. result = _network_get_query(context, session).filter_by(id=id).first()
  2706. if result is None:
  2707. raise exception.ShareNetworkNotFound(share_network_id=id)
  2708. return result
  2709. @require_context
  2710. def share_network_get_all(context):
  2711. return _network_get_query(context).all()
  2712. @require_context
  2713. def share_network_get_all_by_project(context, project_id):
  2714. return _network_get_query(context).filter_by(project_id=project_id).all()
  2715. @require_context
  2716. def share_network_get_all_by_security_service(context, security_service_id):
  2717. session = get_session()
  2718. return (model_query(context, models.ShareNetwork, session=session).
  2719. join(models.ShareNetworkSecurityServiceAssociation,
  2720. models.ShareNetwork.id ==
  2721. models.ShareNetworkSecurityServiceAssociation.share_network_id).
  2722. filter_by(security_service_id=security_service_id, deleted=0)
  2723. .all())
  2724. @require_context
  2725. def share_network_add_security_service(context, id, security_service_id):
  2726. session = get_session()
  2727. with session.begin():
  2728. assoc_ref = (model_query(
  2729. context,
  2730. models.ShareNetworkSecurityServiceAssociation,
  2731. session=session).
  2732. filter_by(share_network_id=id).
  2733. filter_by(
  2734. security_service_id=security_service_id).first())
  2735. if assoc_ref:
  2736. msg = "Already associated"
  2737. raise exception.ShareNetworkSecurityServiceAssociationError(
  2738. share_network_id=id,
  2739. security_service_id=security_service_id,
  2740. reason=msg)
  2741. share_nw_ref = share_network_get(context, id, session=session)
  2742. security_service_ref = security_service_get(context,
  2743. security_service_id,
  2744. session=session)
  2745. share_nw_ref.security_services += [security_service_ref]
  2746. share_nw_ref.save(session=session)
  2747. return share_nw_ref
  2748. @require_context
  2749. def share_network_remove_security_service(context, id, security_service_id):
  2750. session = get_session()
  2751. with session.begin():
  2752. share_nw_ref = share_network_get(context, id, session=session)
  2753. security_service_get(context, security_service_id, session=session)
  2754. assoc_ref = (model_query(
  2755. context,
  2756. models.ShareNetworkSecurityServiceAssociation,
  2757. session=session).
  2758. filter_by(share_network_id=id).
  2759. filter_by(security_service_id=security_service_id).first())
  2760. if assoc_ref:
  2761. assoc_ref.soft_delete(session)
  2762. else:
  2763. msg = "No association defined"
  2764. raise exception.ShareNetworkSecurityServiceDissociationError(
  2765. share_network_id=id,
  2766. security_service_id=security_service_id,
  2767. reason=msg)
  2768. return share_nw_ref
  2769. @require_context
  2770. def count_share_networks(context, project_id, user_id=None,
  2771. share_type_id=None, session=None):
  2772. query = model_query(
  2773. context, models.ShareNetwork,
  2774. func.count(models.ShareNetwork.id),
  2775. read_deleted="no",
  2776. session=session).filter_by(project_id=project_id)
  2777. if share_type_id:
  2778. query = query.join("share_instances").filter_by(
  2779. share_type_id=share_type_id)
  2780. elif user_id is not None:
  2781. query = query.filter_by(user_id=user_id)
  2782. return query.first()[0]
  2783. ###################
  2784. @require_context
  2785. def _network_subnet_get_query(context, session=None):
  2786. if session is None:
  2787. session = get_session()
  2788. return (model_query(context, models.ShareNetworkSubnet, session=session).
  2789. options(joinedload('share_servers'), joinedload('share_network')))
  2790. @require_context
  2791. def share_network_subnet_create(context, values):
  2792. values = ensure_model_dict_has_id(values)
  2793. network_subnet_ref = models.ShareNetworkSubnet()
  2794. network_subnet_ref.update(values)
  2795. session = get_session()
  2796. with session.begin():
  2797. network_subnet_ref.save(session=session)
  2798. return share_network_subnet_get(
  2799. context, network_subnet_ref['id'], session=session)
  2800. @require_context
  2801. def share_network_subnet_delete(context, network_subnet_id):
  2802. session = get_session()
  2803. with session.begin():
  2804. network_subnet_ref = share_network_subnet_get(context,
  2805. network_subnet_id,
  2806. session=session)
  2807. network_subnet_ref.soft_delete(session=session, update_status=True)
  2808. @require_context
  2809. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  2810. def share_network_subnet_update(context, network_subnet_id, values):
  2811. session = get_session()
  2812. with session.begin():
  2813. network_subnet_ref = share_network_subnet_get(context,
  2814. network_subnet_id,
  2815. session=session)
  2816. network_subnet_ref.update(values)
  2817. network_subnet_ref.save(session=session)
  2818. return network_subnet_ref
  2819. @require_context
  2820. def share_network_subnet_get(context, network_subnet_id, session=None):
  2821. result = (_network_subnet_get_query(context, session)
  2822. .filter_by(id=network_subnet_id)
  2823. .first())
  2824. if result is None:
  2825. raise exception.ShareNetworkSubnetNotFound(
  2826. share_network_subnet_id=network_subnet_id)
  2827. return result
  2828. @require_context
  2829. def share_network_subnet_get_all(context):
  2830. return _network_subnet_get_query(context).all()
  2831. @require_context
  2832. def share_network_subnet_get_all_by_share_network(context, network_id):
  2833. return _network_subnet_get_query(context).filter_by(
  2834. share_network_id=network_id).all()
  2835. @require_context
  2836. def share_network_subnet_get_by_availability_zone_id(
  2837. context, share_network_id, availability_zone_id):
  2838. result = (_network_subnet_get_query(context).filter_by(
  2839. share_network_id=share_network_id,
  2840. availability_zone_id=availability_zone_id).first())
  2841. # If a specific subnet wasn't found, try get the default one
  2842. if availability_zone_id and not result:
  2843. return (_network_subnet_get_query(context).filter_by(
  2844. share_network_id=share_network_id,
  2845. availability_zone_id=None).first())
  2846. return result
  2847. @require_context
  2848. def share_network_subnet_get_default_subnet(context, share_network_id):
  2849. return share_network_subnet_get_by_availability_zone_id(
  2850. context, share_network_id, availability_zone_id=None)
  2851. ###################
  2852. def _server_get_query(context, session=None):
  2853. if session is None:
  2854. session = get_session()
  2855. return (model_query(context, models.ShareServer, session=session).
  2856. options(joinedload('share_instances'),
  2857. joinedload('network_allocations'),
  2858. joinedload('share_network_subnet')))
  2859. @require_context
  2860. def share_server_create(context, values):
  2861. values = ensure_model_dict_has_id(values)
  2862. server_ref = models.ShareServer()
  2863. server_ref.update(values)
  2864. session = get_session()
  2865. with session.begin():
  2866. server_ref.save(session=session)
  2867. # NOTE(u_glide): Do so to prevent errors with relationships
  2868. return share_server_get(context, server_ref['id'], session=session)
  2869. @require_context
  2870. def share_server_delete(context, id):
  2871. session = get_session()
  2872. with session.begin():
  2873. server_ref = share_server_get(context, id, session=session)
  2874. share_server_backend_details_delete(context, id, session=session)
  2875. server_ref.soft_delete(session=session, update_status=True)
  2876. @require_context
  2877. def share_server_update(context, id, values):
  2878. session = get_session()
  2879. with session.begin():
  2880. server_ref = share_server_get(context, id, session=session)
  2881. server_ref.update(values)
  2882. server_ref.save(session=session)
  2883. return server_ref
  2884. @require_context
  2885. def share_server_get(context, server_id, session=None):
  2886. result = (_server_get_query(context, session).filter_by(id=server_id)
  2887. .first())
  2888. if result is None:
  2889. raise exception.ShareServerNotFound(share_server_id=server_id)
  2890. return result
  2891. @require_context
  2892. def share_server_search_by_identifier(context, identifier, session=None):
  2893. identifier_field = models.ShareServer.identifier
  2894. # try if given identifier is a substring of existing entry's identifier
  2895. result = (_server_get_query(context, session).filter(
  2896. identifier_field.like('%{}%'.format(identifier))).all())
  2897. if not result:
  2898. # repeat it with underscores instead of hyphens
  2899. result = (_server_get_query(context, session).filter(
  2900. identifier_field.like('%{}%'.format(
  2901. identifier.replace("-", "_")))).all())
  2902. if not result:
  2903. # repeat it with hypens instead of underscores
  2904. result = (_server_get_query(context, session).filter(
  2905. identifier_field.like('%{}%'.format(
  2906. identifier.replace("_", "-")))).all())
  2907. if not result:
  2908. # try if an existing identifier is a substring of given identifier
  2909. result = (_server_get_query(context, session).filter(
  2910. literal(identifier).contains(identifier_field)).all())
  2911. if not result:
  2912. # repeat it with underscores instead of hyphens
  2913. result = (_server_get_query(context, session).filter(
  2914. literal(identifier.replace("-", "_")).contains(
  2915. identifier_field)).all())
  2916. if not result:
  2917. # repeat it with hypens instead of underscores
  2918. result = (_server_get_query(context, session).filter(
  2919. literal(identifier.replace("_", "-")).contains(
  2920. identifier_field)).all())
  2921. if not result:
  2922. raise exception.ShareServerNotFound(share_server_id=identifier)
  2923. return result
  2924. @require_context
  2925. def share_server_get_all_by_host_and_share_subnet_valid(context, host,
  2926. share_subnet_id,
  2927. session=None):
  2928. result = (_server_get_query(context, session).filter_by(host=host)
  2929. .filter_by(share_network_subnet_id=share_subnet_id)
  2930. .filter(models.ShareServer.status.in_(
  2931. (constants.STATUS_CREATING,
  2932. constants.STATUS_ACTIVE))).all())
  2933. if not result:
  2934. filters_description = ('share_network_subnet_id is "%(share_net_id)s",'
  2935. ' host is "%(host)s" and status in'
  2936. ' "%(status_cr)s" or "%(status_act)s"') % {
  2937. 'share_net_id': share_subnet_id,
  2938. 'host': host,
  2939. 'status_cr': constants.STATUS_CREATING,
  2940. 'status_act': constants.STATUS_ACTIVE,
  2941. }
  2942. raise exception.ShareServerNotFoundByFilters(
  2943. filters_description=filters_description)
  2944. return result
  2945. @require_context
  2946. def share_server_get_all(context):
  2947. return _server_get_query(context).all()
  2948. @require_context
  2949. def share_server_get_all_by_host(context, host):
  2950. return _server_get_query(context).filter_by(host=host).all()
  2951. @require_context
  2952. def share_server_get_all_unused_deletable(context, host, updated_before):
  2953. valid_server_status = (
  2954. constants.STATUS_INACTIVE,
  2955. constants.STATUS_ACTIVE,
  2956. constants.STATUS_ERROR,
  2957. )
  2958. result = (_server_get_query(context)
  2959. .filter_by(is_auto_deletable=True)
  2960. .filter_by(host=host)
  2961. .filter(~models.ShareServer.share_groups.any())
  2962. .filter(~models.ShareServer.share_instances.any())
  2963. .filter(models.ShareServer.status.in_(valid_server_status))
  2964. .filter(models.ShareServer.updated_at < updated_before).all())
  2965. return result
  2966. @require_context
  2967. def share_server_backend_details_set(context, share_server_id, server_details):
  2968. share_server_get(context, share_server_id)
  2969. for meta_key, meta_value in server_details.items():
  2970. meta_ref = models.ShareServerBackendDetails()
  2971. meta_ref.update({
  2972. 'key': meta_key,
  2973. 'value': meta_value,
  2974. 'share_server_id': share_server_id
  2975. })
  2976. session = get_session()
  2977. with session.begin():
  2978. meta_ref.save(session)
  2979. return server_details
  2980. @require_context
  2981. def share_server_backend_details_delete(context, share_server_id,
  2982. session=None):
  2983. if not session:
  2984. session = get_session()
  2985. share_server_details = (model_query(context,
  2986. models.ShareServerBackendDetails,
  2987. session=session)
  2988. .filter_by(share_server_id=share_server_id).all())
  2989. for item in share_server_details:
  2990. item.soft_delete(session)
  2991. ###################
  2992. def _driver_private_data_query(session, context, entity_id, key=None,
  2993. read_deleted=False):
  2994. query = model_query(
  2995. context, models.DriverPrivateData, session=session,
  2996. read_deleted=read_deleted,
  2997. ).filter_by(
  2998. entity_uuid=entity_id,
  2999. )
  3000. if isinstance(key, list):
  3001. return query.filter(models.DriverPrivateData.key.in_(key))
  3002. elif key is not None:
  3003. return query.filter_by(key=key)
  3004. return query
  3005. @require_context
  3006. def driver_private_data_get(context, entity_id, key=None,
  3007. default=None, session=None):
  3008. if not session:
  3009. session = get_session()
  3010. query = _driver_private_data_query(session, context, entity_id, key)
  3011. if key is None or isinstance(key, list):
  3012. return {item.key: item.value for item in query.all()}
  3013. else:
  3014. result = query.first()
  3015. return result["value"] if result is not None else default
  3016. @require_context
  3017. def driver_private_data_update(context, entity_id, details,
  3018. delete_existing=False, session=None):
  3019. # NOTE(u_glide): following code modifies details dict, that's why we should
  3020. # copy it
  3021. new_details = copy.deepcopy(details)
  3022. if not session:
  3023. session = get_session()
  3024. with session.begin():
  3025. # Process existing data
  3026. original_data = session.query(models.DriverPrivateData).filter_by(
  3027. entity_uuid=entity_id).all()
  3028. for data_ref in original_data:
  3029. in_new_details = data_ref['key'] in new_details
  3030. if in_new_details:
  3031. new_value = six.text_type(new_details.pop(data_ref['key']))
  3032. data_ref.update({
  3033. "value": new_value,
  3034. "deleted": 0,
  3035. "deleted_at": None
  3036. })
  3037. data_ref.save(session=session)
  3038. elif delete_existing and data_ref['deleted'] != 1:
  3039. data_ref.update({
  3040. "deleted": 1, "deleted_at": timeutils.utcnow()
  3041. })
  3042. data_ref.save(session=session)
  3043. # Add new data
  3044. for key, value in new_details.items():
  3045. data_ref = models.DriverPrivateData()
  3046. data_ref.update({
  3047. "entity_uuid": entity_id,
  3048. "key": key,
  3049. "value": six.text_type(value)
  3050. })
  3051. data_ref.save(session=session)
  3052. return details
  3053. @require_context
  3054. def driver_private_data_delete(context, entity_id, key=None,
  3055. session=None):
  3056. if not session:
  3057. session = get_session()
  3058. with session.begin():
  3059. query = _driver_private_data_query(session, context,
  3060. entity_id, key)
  3061. query.update({"deleted": 1, "deleted_at": timeutils.utcnow()})
  3062. ###################
  3063. @require_context
  3064. def network_allocation_create(context, values):
  3065. values = ensure_model_dict_has_id(values)
  3066. alloc_ref = models.NetworkAllocation()
  3067. alloc_ref.update(values)
  3068. session = get_session()
  3069. with session.begin():
  3070. alloc_ref.save(session=session)
  3071. return alloc_ref
  3072. @require_context
  3073. def network_allocation_delete(context, id):
  3074. session = get_session()
  3075. with session.begin():
  3076. alloc_ref = network_allocation_get(context, id, session=session)
  3077. alloc_ref.soft_delete(session)
  3078. @require_context
  3079. def network_allocation_get(context, id, session=None, read_deleted="no"):
  3080. if session is None:
  3081. session = get_session()
  3082. result = (model_query(context, models.NetworkAllocation, session=session,
  3083. read_deleted=read_deleted).
  3084. filter_by(id=id).first())
  3085. if result is None:
  3086. raise exception.NotFound()
  3087. return result
  3088. @require_context
  3089. def network_allocations_get_by_ip_address(context, ip_address):
  3090. session = get_session()
  3091. result = (model_query(context, models.NetworkAllocation, session=session).
  3092. filter_by(ip_address=ip_address).all())
  3093. return result or []
  3094. @require_context
  3095. def network_allocations_get_for_share_server(context, share_server_id,
  3096. session=None, label=None):
  3097. if session is None:
  3098. session = get_session()
  3099. query = model_query(
  3100. context, models.NetworkAllocation, session=session,
  3101. ).filter_by(
  3102. share_server_id=share_server_id,
  3103. )
  3104. if label:
  3105. if label != 'admin':
  3106. query = query.filter(or_(
  3107. # NOTE(vponomaryov): we treat None as alias for 'user'.
  3108. models.NetworkAllocation.label == None, # noqa
  3109. models.NetworkAllocation.label == label,
  3110. ))
  3111. else:
  3112. query = query.filter(models.NetworkAllocation.label == label)
  3113. result = query.all()
  3114. return result
  3115. @require_context
  3116. def network_allocation_update(context, id, values, read_deleted=None):
  3117. session = get_session()
  3118. with session.begin():
  3119. alloc_ref = network_allocation_get(context, id, session=session,
  3120. read_deleted=read_deleted)
  3121. alloc_ref.update(values)
  3122. alloc_ref.save(session=session)
  3123. return alloc_ref
  3124. ###################
  3125. def _dict_with_specs(inst_type_query, specs_key='extra_specs'):
  3126. """Convert type query result to dict with extra_spec and rate_limit.
  3127. Takes a share [group] type query returned by sqlalchemy and returns it
  3128. as a dictionary, converting the extra/group specs entry from a list
  3129. of dicts:
  3130. 'extra_specs' : [{'key': 'k1', 'value': 'v1', ...}, ...]
  3131. 'group_specs' : [{'key': 'k1', 'value': 'v1', ...}, ...]
  3132. to a single dict:
  3133. 'extra_specs' : {'k1': 'v1'}
  3134. 'group_specs' : {'k1': 'v1'}
  3135. """
  3136. inst_type_dict = dict(inst_type_query)
  3137. specs = {x['key']: x['value'] for x in inst_type_query[specs_key]}
  3138. inst_type_dict[specs_key] = specs
  3139. return inst_type_dict
  3140. @require_admin_context
  3141. def share_type_create(context, values, projects=None):
  3142. """Create a new share type.
  3143. In order to pass in extra specs, the values dict should contain a
  3144. 'extra_specs' key/value pair:
  3145. {'extra_specs' : {'k1': 'v1', 'k2': 'v2', ...}}
  3146. """
  3147. values = ensure_model_dict_has_id(values)
  3148. projects = projects or []
  3149. session = get_session()
  3150. with session.begin():
  3151. try:
  3152. values['extra_specs'] = _metadata_refs(values.get('extra_specs'),
  3153. models.ShareTypeExtraSpecs)
  3154. share_type_ref = models.ShareTypes()
  3155. share_type_ref.update(values)
  3156. share_type_ref.save(session=session)
  3157. except db_exception.DBDuplicateEntry:
  3158. raise exception.ShareTypeExists(id=values['name'])
  3159. except Exception as e:
  3160. raise db_exception.DBError(e)
  3161. for project in set(projects):
  3162. access_ref = models.ShareTypeProjects()
  3163. access_ref.update({"share_type_id": share_type_ref.id,
  3164. "project_id": project})
  3165. access_ref.save(session=session)
  3166. return share_type_ref
  3167. def _share_type_get_query(context, session=None, read_deleted=None,
  3168. expected_fields=None):
  3169. expected_fields = expected_fields or []
  3170. query = (model_query(context,
  3171. models.ShareTypes,
  3172. session=session,
  3173. read_deleted=read_deleted).
  3174. options(joinedload('extra_specs')))
  3175. if 'projects' in expected_fields:
  3176. query = query.options(joinedload('projects'))
  3177. if not context.is_admin:
  3178. the_filter = [models.ShareTypes.is_public == true()]
  3179. projects_attr = getattr(models.ShareTypes, 'projects')
  3180. the_filter.extend([
  3181. projects_attr.any(project_id=context.project_id)
  3182. ])
  3183. query = query.filter(or_(*the_filter))
  3184. return query
  3185. @handle_db_data_error
  3186. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  3187. def _type_update(context, type_id, values, is_group):
  3188. if values.get('name') is None:
  3189. values.pop('name', None)
  3190. if is_group:
  3191. model = models.ShareGroupTypes
  3192. exists_exc = exception.ShareGroupTypeExists
  3193. exists_args = {'type_id': values.get('name')}
  3194. else:
  3195. model = models.ShareTypes
  3196. exists_exc = exception.ShareTypeExists
  3197. exists_args = {'id': values.get('name')}
  3198. session = get_session()
  3199. with session.begin():
  3200. query = model_query(context, model, session=session)
  3201. try:
  3202. result = query.filter_by(id=type_id).update(values)
  3203. except db_exception.DBDuplicateEntry:
  3204. # This exception only occurs if there's a non-deleted
  3205. # share/group type which has the same name as the name being
  3206. # updated.
  3207. raise exists_exc(**exists_args)
  3208. if not result:
  3209. if is_group:
  3210. raise exception.ShareGroupTypeNotFound(type_id=type_id)
  3211. else:
  3212. raise exception.ShareTypeNotFound(share_type_id=type_id)
  3213. def share_type_update(context, share_type_id, values):
  3214. _type_update(context, share_type_id, values, is_group=False)
  3215. @require_context
  3216. def share_type_get_all(context, inactive=False, filters=None):
  3217. """Returns a dict describing all share_types with name as key."""
  3218. filters = filters or {}
  3219. read_deleted = "yes" if inactive else "no"
  3220. query = _share_type_get_query(context, read_deleted=read_deleted)
  3221. if 'is_public' in filters and filters['is_public'] is not None:
  3222. the_filter = [models. ShareTypes.is_public == filters['is_public']]
  3223. if filters['is_public'] and context.project_id is not None:
  3224. projects_attr = getattr(models. ShareTypes, 'projects')
  3225. the_filter.extend([
  3226. projects_attr.any(
  3227. project_id=context.project_id, deleted=0)
  3228. ])
  3229. if len(the_filter) > 1:
  3230. query = query.filter(or_(*the_filter))
  3231. else:
  3232. query = query.filter(the_filter[0])
  3233. rows = query.order_by("name").all()
  3234. result = {}
  3235. for row in rows:
  3236. result[row['name']] = _dict_with_specs(row)
  3237. return result
  3238. def _share_type_get_id_from_share_type_query(context, id, session=None):
  3239. return (model_query(
  3240. context, models.ShareTypes, read_deleted="no", session=session).
  3241. filter_by(id=id))
  3242. def _share_type_get_id_from_share_type(context, id, session=None):
  3243. result = _share_type_get_id_from_share_type_query(
  3244. context, id, session=session).first()
  3245. if not result:
  3246. raise exception.ShareTypeNotFound(share_type_id=id)
  3247. return result['id']
  3248. def _share_type_get(context, id, session=None, inactive=False,
  3249. expected_fields=None):
  3250. expected_fields = expected_fields or []
  3251. read_deleted = "yes" if inactive else "no"
  3252. result = (_share_type_get_query(
  3253. context, session, read_deleted, expected_fields).
  3254. filter_by(id=id).
  3255. first())
  3256. if not result:
  3257. # The only way that id could be None is if the default share type is
  3258. # not configured and no other share type was specified.
  3259. if id is None:
  3260. raise exception.DefaultShareTypeNotConfigured()
  3261. raise exception.ShareTypeNotFound(share_type_id=id)
  3262. share_type = _dict_with_specs(result)
  3263. if 'projects' in expected_fields:
  3264. share_type['projects'] = [p['project_id'] for p in result['projects']]
  3265. return share_type
  3266. @require_context
  3267. def share_type_get(context, id, inactive=False, expected_fields=None):
  3268. """Return a dict describing specific share_type."""
  3269. return _share_type_get(context, id,
  3270. session=None,
  3271. inactive=inactive,
  3272. expected_fields=expected_fields)
  3273. def _share_type_get_by_name(context, name, session=None):
  3274. result = (model_query(context, models.ShareTypes, session=session).
  3275. options(joinedload('extra_specs')).
  3276. filter_by(name=name).
  3277. first())
  3278. if not result:
  3279. raise exception.ShareTypeNotFoundByName(share_type_name=name)
  3280. return _dict_with_specs(result)
  3281. @require_context
  3282. def share_type_get_by_name(context, name):
  3283. """Return a dict describing specific share_type."""
  3284. return _share_type_get_by_name(context, name)
  3285. @require_context
  3286. def share_type_get_by_name_or_id(context, name_or_id):
  3287. """Return a dict describing specific share_type using its name or ID.
  3288. :returns: ShareType object or None if not found
  3289. """
  3290. try:
  3291. return _share_type_get(context, name_or_id)
  3292. except exception.ShareTypeNotFound:
  3293. try:
  3294. return _share_type_get_by_name(context, name_or_id)
  3295. except exception.ShareTypeNotFoundByName:
  3296. return None
  3297. @require_admin_context
  3298. def share_type_destroy(context, id):
  3299. session = get_session()
  3300. with session.begin():
  3301. _share_type_get(context, id, session)
  3302. results = (model_query(context, models.ShareInstance, session=session,
  3303. read_deleted="no").
  3304. filter_by(share_type_id=id).count())
  3305. share_group_count = model_query(
  3306. context,
  3307. models.ShareGroupShareTypeMapping,
  3308. read_deleted="no",
  3309. session=session,
  3310. ).filter_by(share_type_id=id).count()
  3311. if results or share_group_count:
  3312. LOG.error('ShareType %s deletion failed, ShareType in use.',
  3313. id)
  3314. raise exception.ShareTypeInUse(share_type_id=id)
  3315. (model_query(context, models.ShareTypeExtraSpecs, session=session).
  3316. filter_by(share_type_id=id).soft_delete())
  3317. (model_query(context, models.ShareTypes, session=session).
  3318. filter_by(id=id).soft_delete())
  3319. # Destroy any quotas, usages and reservations for the share type:
  3320. quota_destroy_all_by_share_type(context, id)
  3321. def _share_type_access_query(context, session=None):
  3322. return model_query(context, models.ShareTypeProjects, session=session,
  3323. read_deleted="no")
  3324. @require_admin_context
  3325. def share_type_access_get_all(context, type_id):
  3326. share_type_id = _share_type_get_id_from_share_type(context, type_id)
  3327. return (_share_type_access_query(context).
  3328. filter_by(share_type_id=share_type_id).all())
  3329. @require_admin_context
  3330. def share_type_access_add(context, type_id, project_id):
  3331. """Add given tenant to the share type access list."""
  3332. share_type_id = _share_type_get_id_from_share_type(context, type_id)
  3333. access_ref = models.ShareTypeProjects()
  3334. access_ref.update({"share_type_id": share_type_id,
  3335. "project_id": project_id})
  3336. session = get_session()
  3337. with session.begin():
  3338. try:
  3339. access_ref.save(session=session)
  3340. except db_exception.DBDuplicateEntry:
  3341. raise exception.ShareTypeAccessExists(share_type_id=type_id,
  3342. project_id=project_id)
  3343. return access_ref
  3344. @require_admin_context
  3345. def share_type_access_remove(context, type_id, project_id):
  3346. """Remove given tenant from the share type access list."""
  3347. share_type_id = _share_type_get_id_from_share_type(context, type_id)
  3348. count = (_share_type_access_query(context).
  3349. filter_by(share_type_id=share_type_id).
  3350. filter_by(project_id=project_id).
  3351. soft_delete(synchronize_session=False))
  3352. if count == 0:
  3353. raise exception.ShareTypeAccessNotFound(
  3354. share_type_id=type_id, project_id=project_id)
  3355. ####################
  3356. def _share_type_extra_specs_query(context, share_type_id, session=None):
  3357. return (model_query(context, models.ShareTypeExtraSpecs, session=session,
  3358. read_deleted="no").
  3359. filter_by(share_type_id=share_type_id).
  3360. options(joinedload('share_type')))
  3361. @require_context
  3362. def share_type_extra_specs_get(context, share_type_id):
  3363. rows = (_share_type_extra_specs_query(context, share_type_id).
  3364. all())
  3365. result = {}
  3366. for row in rows:
  3367. result[row['key']] = row['value']
  3368. return result
  3369. @require_context
  3370. def share_type_extra_specs_delete(context, share_type_id, key):
  3371. session = get_session()
  3372. with session.begin():
  3373. _share_type_extra_specs_get_item(context, share_type_id, key, session)
  3374. (_share_type_extra_specs_query(context, share_type_id, session).
  3375. filter_by(key=key).soft_delete())
  3376. def _share_type_extra_specs_get_item(context, share_type_id, key,
  3377. session=None):
  3378. result = _share_type_extra_specs_query(
  3379. context, share_type_id, session=session
  3380. ).filter_by(key=key).options(joinedload('share_type')).first()
  3381. if not result:
  3382. raise exception.ShareTypeExtraSpecsNotFound(
  3383. extra_specs_key=key,
  3384. share_type_id=share_type_id)
  3385. return result
  3386. @require_context
  3387. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  3388. def share_type_extra_specs_update_or_create(context, share_type_id, specs):
  3389. session = get_session()
  3390. with session.begin():
  3391. spec_ref = None
  3392. for key, value in specs.items():
  3393. try:
  3394. spec_ref = _share_type_extra_specs_get_item(
  3395. context, share_type_id, key, session)
  3396. except exception.ShareTypeExtraSpecsNotFound:
  3397. spec_ref = models.ShareTypeExtraSpecs()
  3398. spec_ref.update({"key": key, "value": value,
  3399. "share_type_id": share_type_id,
  3400. "deleted": 0})
  3401. spec_ref.save(session=session)
  3402. return specs
  3403. def _ensure_availability_zone_exists(context, values, session, strict=True):
  3404. az_name = values.pop('availability_zone', None)
  3405. if strict and not az_name:
  3406. msg = _("Values dict should have 'availability_zone' field.")
  3407. raise ValueError(msg)
  3408. elif not az_name:
  3409. return
  3410. if uuidutils.is_uuid_like(az_name):
  3411. az_ref = availability_zone_get(context, az_name, session=session)
  3412. else:
  3413. az_ref = availability_zone_create_if_not_exist(
  3414. context, az_name, session=session)
  3415. values.update({'availability_zone_id': az_ref['id']})
  3416. @require_context
  3417. def availability_zone_get(context, id_or_name, session=None):
  3418. if session is None:
  3419. session = get_session()
  3420. query = model_query(context, models.AvailabilityZone, session=session)
  3421. if uuidutils.is_uuid_like(id_or_name):
  3422. query = query.filter_by(id=id_or_name)
  3423. else:
  3424. query = query.filter_by(name=id_or_name)
  3425. result = query.first()
  3426. if not result:
  3427. raise exception.AvailabilityZoneNotFound(id=id_or_name)
  3428. return result
  3429. @require_context
  3430. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  3431. def availability_zone_create_if_not_exist(context, name, session=None):
  3432. if session is None:
  3433. session = get_session()
  3434. az = models.AvailabilityZone()
  3435. az.update({'id': uuidutils.generate_uuid(), 'name': name})
  3436. try:
  3437. with session.begin():
  3438. az.save(session)
  3439. # NOTE(u_glide): Do not catch specific exception here, because it depends
  3440. # on concrete backend used by SqlAlchemy
  3441. except Exception:
  3442. return availability_zone_get(context, name, session=session)
  3443. return az
  3444. @require_context
  3445. def availability_zone_get_all(context):
  3446. session = get_session()
  3447. enabled_services = model_query(
  3448. context, models.Service,
  3449. models.Service.availability_zone_id,
  3450. session=session,
  3451. read_deleted="no"
  3452. ).filter_by(disabled=False).distinct()
  3453. return model_query(context, models.AvailabilityZone, session=session,
  3454. read_deleted="no").filter(
  3455. models.AvailabilityZone.id.in_(enabled_services)
  3456. ).all()
  3457. @require_admin_context
  3458. def purge_deleted_records(context, age_in_days):
  3459. """Purge soft-deleted records older than(and equal) age from tables."""
  3460. if age_in_days < 0:
  3461. msg = _('Must supply a non-negative value for "age_in_days".')
  3462. LOG.error(msg)
  3463. raise exception.InvalidParameterValue(msg)
  3464. metadata = MetaData()
  3465. metadata.reflect(get_engine())
  3466. session = get_session()
  3467. session.begin()
  3468. deleted_age = timeutils.utcnow() - datetime.timedelta(days=age_in_days)
  3469. for table in reversed(metadata.sorted_tables):
  3470. if 'deleted' in table.columns.keys():
  3471. try:
  3472. mds = [m for m in models.__dict__.values() if
  3473. (hasattr(m, '__tablename__') and
  3474. m.__tablename__ == six.text_type(table))]
  3475. if len(mds) > 0:
  3476. # collect all soft-deleted records
  3477. with session.begin_nested():
  3478. model = mds[0]
  3479. s_deleted_records = session.query(model).filter(
  3480. model.deleted_at <= deleted_age)
  3481. deleted_count = 0
  3482. # delete records one by one,
  3483. # skip the records which has FK constraints
  3484. for record in s_deleted_records:
  3485. try:
  3486. with session.begin_nested():
  3487. session.delete(record)
  3488. deleted_count += 1
  3489. except db_exc.DBError:
  3490. LOG.warning(
  3491. ("Deleting soft-deleted resource %s "
  3492. "failed, skipping."), record)
  3493. if deleted_count != 0:
  3494. LOG.info("Deleted %(count)s records in "
  3495. "table %(table)s.",
  3496. {'count': deleted_count, 'table': table})
  3497. except db_exc.DBError:
  3498. LOG.warning("Querying table %s's soft-deleted records "
  3499. "failed, skipping.", table)
  3500. session.commit()
  3501. ####################
  3502. def _share_group_get(context, share_group_id, session=None):
  3503. session = session or get_session()
  3504. result = (model_query(context, models.ShareGroup,
  3505. session=session,
  3506. project_only=True,
  3507. read_deleted='no').
  3508. filter_by(id=share_group_id).
  3509. options(joinedload('share_types')).
  3510. first())
  3511. if not result:
  3512. raise exception.ShareGroupNotFound(share_group_id=share_group_id)
  3513. return result
  3514. @require_context
  3515. def share_group_get(context, share_group_id, session=None):
  3516. return _share_group_get(context, share_group_id, session=session)
  3517. def _share_group_get_all(context, project_id=None, share_server_id=None,
  3518. host=None, detailed=True, filters=None,
  3519. sort_key=None, sort_dir=None, session=None):
  3520. session = session or get_session()
  3521. sort_key = sort_key or 'created_at'
  3522. sort_dir = sort_dir or 'desc'
  3523. query = model_query(
  3524. context, models.ShareGroup, session=session, read_deleted='no')
  3525. # Apply filters
  3526. if not filters:
  3527. filters = {}
  3528. no_key = 'key_is_absent'
  3529. for k, v in filters.items():
  3530. temp_k = k.rstrip('~') if k in constants.LIKE_FILTER else k
  3531. filter_attr = getattr(models.ShareGroup, temp_k, no_key)
  3532. if filter_attr == no_key:
  3533. msg = _("Share groups cannot be filtered using '%s' key.")
  3534. raise exception.InvalidInput(reason=msg % k)
  3535. if k in constants.LIKE_FILTER:
  3536. query = query.filter(filter_attr.op('LIKE')(u'%' + v + u'%'))
  3537. else:
  3538. query = query.filter(filter_attr == v)
  3539. if project_id:
  3540. query = query.filter(
  3541. models.ShareGroup.project_id == project_id)
  3542. if host:
  3543. query = query.filter(
  3544. models.ShareGroup.host == host)
  3545. if share_server_id:
  3546. query = query.filter(
  3547. models.ShareGroup.share_server_id == share_server_id)
  3548. try:
  3549. query = apply_sorting(models.ShareGroup, query, sort_key, sort_dir)
  3550. except AttributeError:
  3551. msg = _("Wrong sorting key provided - '%s'.") % sort_key
  3552. raise exception.InvalidInput(reason=msg)
  3553. if detailed:
  3554. return query.options(joinedload('share_types')).all()
  3555. else:
  3556. query = query.with_entities(
  3557. models.ShareGroup.id, models.ShareGroup.name)
  3558. values = []
  3559. for sg_id, sg_name in query.all():
  3560. values.append({"id": sg_id, "name": sg_name})
  3561. return values
  3562. @require_admin_context
  3563. def share_group_get_all(context, detailed=True, filters=None, sort_key=None,
  3564. sort_dir=None):
  3565. return _share_group_get_all(
  3566. context, detailed=detailed, filters=filters,
  3567. sort_key=sort_key, sort_dir=sort_dir)
  3568. @require_admin_context
  3569. def share_group_get_all_by_host(context, host, detailed=True):
  3570. return _share_group_get_all(context, host=host, detailed=detailed)
  3571. @require_context
  3572. def share_group_get_all_by_project(context, project_id, detailed=True,
  3573. filters=None, sort_key=None, sort_dir=None):
  3574. authorize_project_context(context, project_id)
  3575. return _share_group_get_all(
  3576. context, project_id=project_id, detailed=detailed, filters=filters,
  3577. sort_key=sort_key, sort_dir=sort_dir)
  3578. @require_context
  3579. def share_group_get_all_by_share_server(context, share_server_id, filters=None,
  3580. sort_key=None, sort_dir=None):
  3581. return _share_group_get_all(
  3582. context, share_server_id=share_server_id, filters=filters,
  3583. sort_key=sort_key, sort_dir=sort_dir)
  3584. @require_context
  3585. def share_group_create(context, values):
  3586. share_group = models.ShareGroup()
  3587. if not values.get('id'):
  3588. values['id'] = six.text_type(uuidutils.generate_uuid())
  3589. mappings = []
  3590. for item in values.get('share_types') or []:
  3591. mapping = models.ShareGroupShareTypeMapping()
  3592. mapping['id'] = six.text_type(uuidutils.generate_uuid())
  3593. mapping['share_type_id'] = item
  3594. mapping['share_group_id'] = values['id']
  3595. mappings.append(mapping)
  3596. values['share_types'] = mappings
  3597. session = get_session()
  3598. with session.begin():
  3599. share_group.update(values)
  3600. session.add(share_group)
  3601. return _share_group_get(context, values['id'], session=session)
  3602. @require_context
  3603. def share_group_update(context, share_group_id, values):
  3604. session = get_session()
  3605. with session.begin():
  3606. share_group_ref = _share_group_get(
  3607. context, share_group_id, session=session)
  3608. share_group_ref.update(values)
  3609. share_group_ref.save(session=session)
  3610. return share_group_ref
  3611. @require_admin_context
  3612. def share_group_destroy(context, share_group_id):
  3613. session = get_session()
  3614. with session.begin():
  3615. share_group_ref = _share_group_get(
  3616. context, share_group_id, session=session)
  3617. share_group_ref.soft_delete(session)
  3618. session.query(models.ShareGroupShareTypeMapping).filter_by(
  3619. share_group_id=share_group_ref['id']).soft_delete()
  3620. @require_context
  3621. def count_shares_in_share_group(context, share_group_id, session=None):
  3622. session = session or get_session()
  3623. return (model_query(context, models.Share, session=session,
  3624. project_only=True, read_deleted="no").
  3625. filter_by(share_group_id=share_group_id).
  3626. count())
  3627. @require_context
  3628. def get_all_shares_by_share_group(context, share_group_id, session=None):
  3629. session = session or get_session()
  3630. return (model_query(
  3631. context, models.Share, session=session,
  3632. project_only=True, read_deleted="no").
  3633. filter_by(share_group_id=share_group_id).
  3634. all())
  3635. @require_context
  3636. def count_share_groups(context, project_id, user_id=None,
  3637. share_type_id=None, session=None):
  3638. query = model_query(
  3639. context, models.ShareGroup,
  3640. func.count(models.ShareGroup.id),
  3641. read_deleted="no",
  3642. session=session).filter_by(project_id=project_id)
  3643. if share_type_id:
  3644. query = query.join("share_group_share_type_mappings").filter_by(
  3645. share_type_id=share_type_id)
  3646. elif user_id is not None:
  3647. query = query.filter_by(user_id=user_id)
  3648. return query.first()[0]
  3649. @require_context
  3650. def count_share_group_snapshots(context, project_id, user_id=None,
  3651. share_type_id=None, session=None):
  3652. query = model_query(
  3653. context, models.ShareGroupSnapshot,
  3654. func.count(models.ShareGroupSnapshot.id),
  3655. read_deleted="no",
  3656. session=session).filter_by(project_id=project_id)
  3657. if share_type_id:
  3658. query = query.join(
  3659. "share_group"
  3660. ).join(
  3661. "share_group_share_type_mappings"
  3662. ).filter_by(share_type_id=share_type_id)
  3663. elif user_id is not None:
  3664. query = query.filter_by(user_id=user_id)
  3665. return query.first()[0]
  3666. @require_context
  3667. def count_share_group_snapshots_in_share_group(context, share_group_id,
  3668. session=None):
  3669. session = session or get_session()
  3670. return model_query(
  3671. context, models.ShareGroupSnapshot, session=session,
  3672. project_only=True, read_deleted="no",
  3673. ).filter_by(
  3674. share_group_id=share_group_id,
  3675. ).count()
  3676. @require_context
  3677. def count_share_groups_in_share_network(context, share_network_id,
  3678. session=None):
  3679. session = session or get_session()
  3680. return (model_query(
  3681. context, models.ShareGroup, session=session,
  3682. project_only=True, read_deleted="no").
  3683. filter_by(share_network_id=share_network_id).
  3684. count())
  3685. @require_context
  3686. def count_share_group_snapshot_members_in_share(context, share_id,
  3687. session=None):
  3688. session = session or get_session()
  3689. return model_query(
  3690. context, models.ShareSnapshotInstance, session=session,
  3691. project_only=True, read_deleted="no",
  3692. ).join(
  3693. models.ShareInstance,
  3694. models.ShareInstance.id == (
  3695. models.ShareSnapshotInstance.share_instance_id),
  3696. ).filter(
  3697. models.ShareInstance.share_id == share_id,
  3698. ).count()
  3699. @require_context
  3700. def _share_group_snapshot_get(context, share_group_snapshot_id, session=None):
  3701. session = session or get_session()
  3702. result = model_query(
  3703. context, models.ShareGroupSnapshot, session=session,
  3704. project_only=True, read_deleted='no',
  3705. ).options(
  3706. joinedload('share_group'),
  3707. joinedload('share_group_snapshot_members'),
  3708. ).filter_by(
  3709. id=share_group_snapshot_id,
  3710. ).first()
  3711. if not result:
  3712. raise exception.ShareGroupSnapshotNotFound(
  3713. share_group_snapshot_id=share_group_snapshot_id)
  3714. return result
  3715. def _share_group_snapshot_get_all(
  3716. context, project_id=None, detailed=True, filters=None,
  3717. sort_key=None, sort_dir=None, session=None):
  3718. session = session or get_session()
  3719. if not sort_key:
  3720. sort_key = 'created_at'
  3721. if not sort_dir:
  3722. sort_dir = 'desc'
  3723. query = model_query(
  3724. context, models.ShareGroupSnapshot, session=session, read_deleted='no',
  3725. ).options(
  3726. joinedload('share_group'),
  3727. joinedload('share_group_snapshot_members'),
  3728. )
  3729. # Apply filters
  3730. if not filters:
  3731. filters = {}
  3732. no_key = 'key_is_absent'
  3733. for k, v in filters.items():
  3734. filter_attr = getattr(models.ShareGroupSnapshot, k, no_key)
  3735. if filter_attr == no_key:
  3736. msg = _("Share group snapshots cannot be filtered using '%s' key.")
  3737. raise exception.InvalidInput(reason=msg % k)
  3738. query = query.filter(filter_attr == v)
  3739. if project_id:
  3740. query = query.filter(
  3741. models.ShareGroupSnapshot.project_id == project_id)
  3742. try:
  3743. query = apply_sorting(
  3744. models.ShareGroupSnapshot, query, sort_key, sort_dir)
  3745. except AttributeError:
  3746. msg = _("Wrong sorting key provided - '%s'.") % sort_key
  3747. raise exception.InvalidInput(reason=msg)
  3748. if detailed:
  3749. return query.all()
  3750. else:
  3751. query = query.with_entities(models.ShareGroupSnapshot.id,
  3752. models.ShareGroupSnapshot.name)
  3753. values = []
  3754. for sgs_id, sgs_name in query.all():
  3755. values.append({"id": sgs_id, "name": sgs_name})
  3756. return values
  3757. @require_context
  3758. def share_group_snapshot_get(context, share_group_snapshot_id, session=None):
  3759. session = session or get_session()
  3760. return _share_group_snapshot_get(
  3761. context, share_group_snapshot_id, session=session)
  3762. @require_admin_context
  3763. def share_group_snapshot_get_all(
  3764. context, detailed=True, filters=None, sort_key=None, sort_dir=None):
  3765. return _share_group_snapshot_get_all(
  3766. context, filters=filters, detailed=detailed,
  3767. sort_key=sort_key, sort_dir=sort_dir)
  3768. @require_context
  3769. def share_group_snapshot_get_all_by_project(
  3770. context, project_id, detailed=True, filters=None,
  3771. sort_key=None, sort_dir=None):
  3772. authorize_project_context(context, project_id)
  3773. return _share_group_snapshot_get_all(
  3774. context, project_id=project_id, filters=filters, detailed=detailed,
  3775. sort_key=sort_key, sort_dir=sort_dir,
  3776. )
  3777. @require_context
  3778. def share_group_snapshot_create(context, values):
  3779. share_group_snapshot = models.ShareGroupSnapshot()
  3780. if not values.get('id'):
  3781. values['id'] = six.text_type(uuidutils.generate_uuid())
  3782. session = get_session()
  3783. with session.begin():
  3784. share_group_snapshot.update(values)
  3785. session.add(share_group_snapshot)
  3786. return _share_group_snapshot_get(
  3787. context, values['id'], session=session)
  3788. @require_context
  3789. def share_group_snapshot_update(context, share_group_snapshot_id, values):
  3790. session = get_session()
  3791. with session.begin():
  3792. share_group_ref = _share_group_snapshot_get(
  3793. context, share_group_snapshot_id, session=session)
  3794. share_group_ref.update(values)
  3795. share_group_ref.save(session=session)
  3796. return share_group_ref
  3797. @require_admin_context
  3798. def share_group_snapshot_destroy(context, share_group_snapshot_id):
  3799. session = get_session()
  3800. with session.begin():
  3801. share_group_snap_ref = _share_group_snapshot_get(
  3802. context, share_group_snapshot_id, session=session)
  3803. share_group_snap_ref.soft_delete(session)
  3804. session.query(models.ShareSnapshotInstance).filter_by(
  3805. share_group_snapshot_id=share_group_snapshot_id).soft_delete()
  3806. @require_context
  3807. def share_group_snapshot_members_get_all(context, share_group_snapshot_id,
  3808. session=None):
  3809. session = session or get_session()
  3810. query = model_query(
  3811. context, models.ShareSnapshotInstance, session=session,
  3812. read_deleted='no',
  3813. ).filter_by(share_group_snapshot_id=share_group_snapshot_id)
  3814. return query.all()
  3815. @require_context
  3816. def share_group_snapshot_member_get(context, member_id, session=None):
  3817. result = model_query(
  3818. context, models.ShareSnapshotInstance, session=session,
  3819. project_only=True, read_deleted='no',
  3820. ).filter_by(id=member_id).first()
  3821. if not result:
  3822. raise exception.ShareGroupSnapshotMemberNotFound(member_id=member_id)
  3823. return result
  3824. @require_context
  3825. def share_group_snapshot_member_create(context, values):
  3826. member = models.ShareSnapshotInstance()
  3827. if not values.get('id'):
  3828. values['id'] = six.text_type(uuidutils.generate_uuid())
  3829. _change_size_to_instance_size(values)
  3830. session = get_session()
  3831. with session.begin():
  3832. member.update(values)
  3833. session.add(member)
  3834. return share_group_snapshot_member_get(
  3835. context, values['id'], session=session)
  3836. @require_context
  3837. def share_group_snapshot_member_update(context, member_id, values):
  3838. session = get_session()
  3839. _change_size_to_instance_size(values)
  3840. with session.begin():
  3841. member = share_group_snapshot_member_get(
  3842. context, member_id, session=session)
  3843. member.update(values)
  3844. session.add(member)
  3845. return share_group_snapshot_member_get(
  3846. context, member_id, session=session)
  3847. ####################
  3848. @require_admin_context
  3849. def share_group_type_create(context, values, projects=None):
  3850. """Create a new share group type.
  3851. In order to pass in group specs, the values dict should contain a
  3852. 'group_specs' key/value pair:
  3853. {'group_specs' : {'k1': 'v1', 'k2': 'v2', ...}}
  3854. """
  3855. values = ensure_model_dict_has_id(values)
  3856. projects = projects or []
  3857. session = get_session()
  3858. with session.begin():
  3859. try:
  3860. values['group_specs'] = _metadata_refs(
  3861. values.get('group_specs'), models.ShareGroupTypeSpecs)
  3862. mappings = []
  3863. for item in values.get('share_types', []):
  3864. share_type = share_type_get_by_name_or_id(context, item)
  3865. if not share_type:
  3866. raise exception.ShareTypeDoesNotExist(share_type=item)
  3867. mapping = models.ShareGroupTypeShareTypeMapping()
  3868. mapping['id'] = six.text_type(uuidutils.generate_uuid())
  3869. mapping['share_type_id'] = share_type['id']
  3870. mapping['share_group_type_id'] = values['id']
  3871. mappings.append(mapping)
  3872. values['share_types'] = mappings
  3873. share_group_type_ref = models.ShareGroupTypes()
  3874. share_group_type_ref.update(values)
  3875. share_group_type_ref.save(session=session)
  3876. except db_exception.DBDuplicateEntry:
  3877. raise exception.ShareGroupTypeExists(type_id=values['name'])
  3878. except exception.ShareTypeDoesNotExist:
  3879. raise
  3880. except Exception as e:
  3881. raise db_exception.DBError(e)
  3882. for project in set(projects):
  3883. access_ref = models.ShareGroupTypeProjects()
  3884. access_ref.update({"share_group_type_id": share_group_type_ref.id,
  3885. "project_id": project})
  3886. access_ref.save(session=session)
  3887. return share_group_type_ref
  3888. def _share_group_type_get_query(context, session=None, read_deleted=None,
  3889. expected_fields=None):
  3890. expected_fields = expected_fields or []
  3891. query = model_query(
  3892. context, models.ShareGroupTypes, session=session,
  3893. read_deleted=read_deleted
  3894. ).options(
  3895. joinedload('group_specs'),
  3896. joinedload('share_types'),
  3897. )
  3898. if 'projects' in expected_fields:
  3899. query = query.options(joinedload('projects'))
  3900. if not context.is_admin:
  3901. the_filter = [models.ShareGroupTypes.is_public == true()]
  3902. projects_attr = getattr(models.ShareGroupTypes, 'projects')
  3903. the_filter.extend([
  3904. projects_attr.any(project_id=context.project_id)
  3905. ])
  3906. query = query.filter(or_(*the_filter))
  3907. return query
  3908. @require_context
  3909. def share_group_type_get_all(context, inactive=False, filters=None):
  3910. """Returns a dict describing all share group types with name as key."""
  3911. filters = filters or {}
  3912. read_deleted = "yes" if inactive else "no"
  3913. query = _share_group_type_get_query(context, read_deleted=read_deleted)
  3914. if 'is_public' in filters and filters['is_public'] is not None:
  3915. the_filter = [models.ShareGroupTypes.is_public == filters['is_public']]
  3916. if filters['is_public'] and context.project_id is not None:
  3917. projects_attr = getattr(models. ShareGroupTypes, 'projects')
  3918. the_filter.extend([
  3919. projects_attr.any(
  3920. project_id=context.project_id, deleted=0)
  3921. ])
  3922. if len(the_filter) > 1:
  3923. query = query.filter(or_(*the_filter))
  3924. else:
  3925. query = query.filter(the_filter[0])
  3926. rows = query.order_by("name").all()
  3927. result = {}
  3928. for row in rows:
  3929. result[row['name']] = _dict_with_specs(row, 'group_specs')
  3930. return result
  3931. def _share_group_type_get_id_from_share_group_type_query(context, type_id,
  3932. session=None):
  3933. return model_query(
  3934. context, models.ShareGroupTypes, read_deleted="no", session=session,
  3935. ).filter_by(id=type_id)
  3936. def _share_group_type_get_id_from_share_group_type(context, type_id,
  3937. session=None):
  3938. result = _share_group_type_get_id_from_share_group_type_query(
  3939. context, type_id, session=session).first()
  3940. if not result:
  3941. raise exception.ShareGroupTypeNotFound(type_id=type_id)
  3942. return result['id']
  3943. @require_context
  3944. def _share_group_type_get(context, type_id, session=None, inactive=False,
  3945. expected_fields=None):
  3946. expected_fields = expected_fields or []
  3947. read_deleted = "yes" if inactive else "no"
  3948. result = _share_group_type_get_query(
  3949. context, session, read_deleted, expected_fields,
  3950. ).filter_by(id=type_id).first()
  3951. if not result:
  3952. raise exception.ShareGroupTypeNotFound(type_id=type_id)
  3953. share_group_type = _dict_with_specs(result, 'group_specs')
  3954. if 'projects' in expected_fields:
  3955. share_group_type['projects'] = [
  3956. p['project_id'] for p in result['projects']]
  3957. return share_group_type
  3958. @require_context
  3959. def share_group_type_get(context, type_id, inactive=False,
  3960. expected_fields=None):
  3961. """Return a dict describing specific share group type."""
  3962. return _share_group_type_get(
  3963. context, type_id, session=None, inactive=inactive,
  3964. expected_fields=expected_fields)
  3965. @require_context
  3966. def _share_group_type_get_by_name(context, name, session=None):
  3967. result = model_query(
  3968. context, models.ShareGroupTypes, session=session,
  3969. ).options(
  3970. joinedload('group_specs'),
  3971. joinedload('share_types'),
  3972. ).filter_by(
  3973. name=name,
  3974. ).first()
  3975. if not result:
  3976. raise exception.ShareGroupTypeNotFoundByName(type_name=name)
  3977. return _dict_with_specs(result, 'group_specs')
  3978. @require_context
  3979. def share_group_type_get_by_name(context, name):
  3980. """Return a dict describing specific share group type."""
  3981. return _share_group_type_get_by_name(context, name)
  3982. @require_admin_context
  3983. def share_group_type_destroy(context, type_id):
  3984. session = get_session()
  3985. with session.begin():
  3986. _share_group_type_get(context, type_id, session)
  3987. results = model_query(
  3988. context, models.ShareGroup, session=session, read_deleted="no",
  3989. ).filter_by(
  3990. share_group_type_id=type_id,
  3991. ).count()
  3992. if results:
  3993. LOG.error('Share group type %s deletion failed, it in use.',
  3994. type_id)
  3995. raise exception.ShareGroupTypeInUse(type_id=type_id)
  3996. model_query(
  3997. context, models.ShareGroupTypeSpecs, session=session,
  3998. ).filter_by(
  3999. share_group_type_id=type_id,
  4000. ).soft_delete()
  4001. model_query(
  4002. context, models.ShareGroupTypes, session=session
  4003. ).filter_by(
  4004. id=type_id,
  4005. ).soft_delete()
  4006. def _share_group_type_access_query(context, session=None):
  4007. return model_query(context, models.ShareGroupTypeProjects, session=session,
  4008. read_deleted="no")
  4009. @require_admin_context
  4010. def share_group_type_access_get_all(context, type_id):
  4011. share_group_type_id = _share_group_type_get_id_from_share_group_type(
  4012. context, type_id)
  4013. return _share_group_type_access_query(context).filter_by(
  4014. share_group_type_id=share_group_type_id,
  4015. ).all()
  4016. @require_admin_context
  4017. def share_group_type_access_add(context, type_id, project_id):
  4018. """Add given tenant to the share group type access list."""
  4019. share_group_type_id = _share_group_type_get_id_from_share_group_type(
  4020. context, type_id)
  4021. access_ref = models.ShareGroupTypeProjects()
  4022. access_ref.update({"share_group_type_id": share_group_type_id,
  4023. "project_id": project_id})
  4024. session = get_session()
  4025. with session.begin():
  4026. try:
  4027. access_ref.save(session=session)
  4028. except db_exception.DBDuplicateEntry:
  4029. raise exception.ShareGroupTypeAccessExists(
  4030. type_id=share_group_type_id, project_id=project_id)
  4031. return access_ref
  4032. @require_admin_context
  4033. def share_group_type_access_remove(context, type_id, project_id):
  4034. """Remove given tenant from the share group type access list."""
  4035. share_group_type_id = _share_group_type_get_id_from_share_group_type(
  4036. context, type_id)
  4037. count = _share_group_type_access_query(context).filter_by(
  4038. share_group_type_id=share_group_type_id,
  4039. ).filter_by(
  4040. project_id=project_id,
  4041. ).soft_delete(
  4042. synchronize_session=False,
  4043. )
  4044. if count == 0:
  4045. raise exception.ShareGroupTypeAccessNotFound(
  4046. type_id=share_group_type_id, project_id=project_id)
  4047. def _share_group_type_specs_query(context, type_id, session=None):
  4048. return model_query(
  4049. context, models.ShareGroupTypeSpecs, session=session, read_deleted="no"
  4050. ).filter_by(
  4051. share_group_type_id=type_id,
  4052. ).options(
  4053. joinedload('share_group_type'),
  4054. )
  4055. @require_context
  4056. def share_group_type_specs_get(context, type_id):
  4057. rows = _share_group_type_specs_query(context, type_id).all()
  4058. result = {}
  4059. for row in rows:
  4060. result[row['key']] = row['value']
  4061. return result
  4062. @require_context
  4063. def share_group_type_specs_delete(context, type_id, key):
  4064. session = get_session()
  4065. with session.begin():
  4066. _share_group_type_specs_get_item(context, type_id, key, session)
  4067. _share_group_type_specs_query(
  4068. context, type_id, session,
  4069. ).filter_by(
  4070. key=key,
  4071. ).soft_delete()
  4072. @require_context
  4073. def _share_group_type_specs_get_item(context, type_id, key, session=None):
  4074. result = _share_group_type_specs_query(
  4075. context, type_id, session=session,
  4076. ).filter_by(
  4077. key=key,
  4078. ).options(
  4079. joinedload('share_group_type'),
  4080. ).first()
  4081. if not result:
  4082. raise exception.ShareGroupTypeSpecsNotFound(
  4083. specs_key=key, type_id=type_id)
  4084. return result
  4085. @require_context
  4086. @oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
  4087. def share_group_type_specs_update_or_create(context, type_id, specs):
  4088. session = get_session()
  4089. with session.begin():
  4090. spec_ref = None
  4091. for key, value in specs.items():
  4092. try:
  4093. spec_ref = _share_group_type_specs_get_item(
  4094. context, type_id, key, session)
  4095. except exception.ShareGroupTypeSpecsNotFound:
  4096. spec_ref = models.ShareGroupTypeSpecs()
  4097. spec_ref.update({"key": key, "value": value,
  4098. "share_group_type_id": type_id, "deleted": 0})
  4099. spec_ref.save(session=session)
  4100. return specs
  4101. ###############################
  4102. @require_context
  4103. def message_get(context, message_id):
  4104. query = model_query(context,
  4105. models.Message,
  4106. read_deleted="no",
  4107. project_only="yes")
  4108. result = query.filter_by(id=message_id).first()
  4109. if not result:
  4110. raise exception.MessageNotFound(message_id=message_id)
  4111. return result
  4112. @require_context
  4113. def message_get_all(context, filters=None, limit=None, offset=None,
  4114. sort_key='created_at', sort_dir='desc'):
  4115. """Retrieves all messages.
  4116. If no sort parameters are specified then the returned messages are
  4117. sorted by the 'created_at' key in descending order.
  4118. :param context: context to query under
  4119. :param limit: maximum number of items to return
  4120. :param offset: the number of items to skip from the marker or from the
  4121. first element.
  4122. :param sort_key: attributes by which results should be sorted.
  4123. :param sort_dir: directions in which results should be sorted.
  4124. :param filters: dictionary of filters; values that are in lists, tuples,
  4125. or sets cause an 'IN' operation, while exact matching
  4126. is used for other values, see exact_filter function for
  4127. more information
  4128. :returns: list of matching messages
  4129. """
  4130. messages = models.Message
  4131. session = get_session()
  4132. with session.begin():
  4133. query = model_query(context,
  4134. messages,
  4135. read_deleted="no",
  4136. project_only="yes")
  4137. legal_filter_keys = ('request_id', 'resource_type', 'resource_id',
  4138. 'action_id', 'detail_id', 'message_level',
  4139. 'created_since', 'created_before')
  4140. if not filters:
  4141. filters = {}
  4142. query = exact_filter(query, messages, filters, legal_filter_keys)
  4143. query = utils.paginate_query(query, messages, limit,
  4144. sort_key=sort_key,
  4145. sort_dir=sort_dir,
  4146. offset=offset)
  4147. return query.all()
  4148. @require_context
  4149. def message_create(context, message_values):
  4150. values = copy.deepcopy(message_values)
  4151. message_ref = models.Message()
  4152. if not values.get('id'):
  4153. values['id'] = uuidutils.generate_uuid()
  4154. message_ref.update(values)
  4155. session = get_session()
  4156. with session.begin():
  4157. session.add(message_ref)
  4158. return message_get(context, message_ref['id'])
  4159. @require_context
  4160. def message_destroy(context, message):
  4161. session = get_session()
  4162. with session.begin():
  4163. (model_query(context, models.Message, session=session).
  4164. filter_by(id=message.get('id')).soft_delete())
  4165. @require_admin_context
  4166. def cleanup_expired_messages(context):
  4167. session = get_session()
  4168. now = timeutils.utcnow()
  4169. with session.begin():
  4170. return session.query(models.Message).filter(
  4171. models.Message.expires_at < now).delete()
  4172. @require_context
  4173. def backend_info_get(context, host):
  4174. """Get hash info for given host."""
  4175. session = get_session()
  4176. result = _backend_info_query(session, context, host)
  4177. return result
  4178. @require_context
  4179. def backend_info_create(context, host, value):
  4180. session = get_session()
  4181. with session.begin():
  4182. info_ref = models.BackendInfo()
  4183. info_ref.update({"host": host,
  4184. "info_hash": value})
  4185. info_ref.save(session)
  4186. return info_ref
  4187. @require_context
  4188. def backend_info_update(context, host, value=None, delete_existing=False):
  4189. """Remove backend info for host name."""
  4190. session = get_session()
  4191. with session.begin():
  4192. info_ref = _backend_info_query(session, context, host)
  4193. if info_ref:
  4194. if value:
  4195. info_ref.update({"info_hash": value})
  4196. elif delete_existing and info_ref['deleted'] != 1:
  4197. info_ref.update({"deleted": 1,
  4198. "deleted_at": timeutils.utcnow()})
  4199. else:
  4200. info_ref = models.BackendInfo()
  4201. info_ref.update({"host": host,
  4202. "info_hash": value})
  4203. info_ref.save(session)
  4204. return info_ref
  4205. def _backend_info_query(session, context, host, read_deleted=False):
  4206. result = model_query(
  4207. context, models.BackendInfo, session=session,
  4208. read_deleted=read_deleted,
  4209. ).filter_by(
  4210. host=host,
  4211. ).first()
  4212. return result