Coverage for manila/share/snapshot_access.py: 94%
57 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright (c) 2016 Hitachi Data Systems
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16from oslo_log import log
18from manila.common import constants
19from manila import utils
21LOG = log.getLogger(__name__)
24class ShareSnapshotInstanceAccess(object):
26 def __init__(self, db, driver):
27 self.db = db
28 self.driver = driver
30 def update_access_rules(self, context, snapshot_instance_id,
31 delete_all_rules=False, share_server=None):
32 """Update driver and database access rules for given snapshot instance.
34 :param context: current context
35 :param snapshot_instance_id: Id of the snapshot instance model
36 :param delete_all_rules: Whether all rules should be deleted.
37 :param share_server: Share server model or None
38 """
39 snapshot_instance = self.db.share_snapshot_instance_get(
40 context, snapshot_instance_id, with_share_data=True)
41 snapshot_id = snapshot_instance['snapshot_id']
43 @utils.synchronized(
44 "update_access_rules_for_snapshot_%s" % snapshot_id, external=True)
45 def _update_access_rules_locked(*args, **kwargs):
46 return self._update_access_rules(*args, **kwargs)
48 _update_access_rules_locked(
49 context=context,
50 snapshot_instance=snapshot_instance,
51 delete_all_rules=delete_all_rules,
52 share_server=share_server,
53 )
55 def _update_access_rules(self, context, snapshot_instance,
56 delete_all_rules=None, share_server=None):
58 # NOTE(ganso): First let's get all the rules and the mappings.
60 rules = self.db.share_snapshot_access_get_all_for_snapshot_instance(
61 context, snapshot_instance['id'])
63 add_rules = []
64 delete_rules = []
66 if delete_all_rules:
67 # NOTE(ganso): We want to delete all rules.
68 delete_rules = rules
69 rules_to_be_on_snapshot = []
70 # NOTE(ganso): We select all deletable mappings.
71 for rule in rules:
72 # NOTE(ganso): No need to update the state if already set.
73 if rule['state'] != constants.ACCESS_STATE_DENYING: 73 ↛ 71line 73 didn't jump to line 71 because the condition on line 73 was always true
74 self.db.share_snapshot_instance_access_update(
75 context, rule['access_id'], snapshot_instance['id'],
76 {'state': constants.ACCESS_STATE_DENYING})
78 else:
80 # NOTE(ganso): error'ed rules are to be left alone until
81 # reset back to "queued_to_deny" by API.
82 rules_to_be_on_snapshot = [
83 r for r in rules if r['state'] not in (
84 constants.ACCESS_STATE_QUEUED_TO_DENY,
85 # NOTE(ganso): We select denying rules as a recovery
86 # mechanism for invalid rules during a restart.
87 constants.ACCESS_STATE_DENYING,
88 # NOTE(ganso): We do not re-send error-ed access rules to
89 # drivers.
90 constants.ACCESS_STATE_ERROR
91 )
92 ]
94 # NOTE(ganso): Process queued rules
95 for rule in rules:
96 # NOTE(ganso): We are barely handling recovery, so if any rule
97 # exists in 'applying' or 'denying' state, we add them again.
98 if rule['state'] in (constants.ACCESS_STATE_QUEUED_TO_APPLY,
99 constants.ACCESS_STATE_APPLYING):
100 if rule['state'] == (
101 constants.ACCESS_STATE_QUEUED_TO_APPLY):
102 self.db.share_snapshot_instance_access_update(
103 context, rule['access_id'],
104 snapshot_instance['id'],
105 {'state': constants.ACCESS_STATE_APPLYING})
106 add_rules.append(rule)
107 elif rule['state'] in (
108 constants.ACCESS_STATE_QUEUED_TO_DENY,
109 constants.ACCESS_STATE_DENYING):
110 if rule['state'] == ( 110 ↛ 116line 110 didn't jump to line 116 because the condition on line 110 was always true
111 constants.ACCESS_STATE_QUEUED_TO_DENY):
112 self.db.share_snapshot_instance_access_update(
113 context, rule['access_id'],
114 snapshot_instance['id'],
115 {'state': constants.ACCESS_STATE_DENYING})
116 delete_rules.append(rule)
118 try:
119 self.driver.snapshot_update_access(
120 context,
121 snapshot_instance,
122 rules_to_be_on_snapshot,
123 add_rules=add_rules,
124 delete_rules=delete_rules,
125 share_server=share_server)
127 # NOTE(ganso): successfully added rules transition to "active".
128 for rule in add_rules:
129 self.db.share_snapshot_instance_access_update(
130 context, rule['access_id'], snapshot_instance['id'],
131 {'state': constants.STATUS_ACTIVE})
133 except Exception:
134 # NOTE(ganso): if we failed, we set all the transitional rules
135 # to ERROR.
136 for rule in add_rules + delete_rules:
137 self.db.share_snapshot_instance_access_update(
138 context, rule['access_id'], snapshot_instance['id'],
139 {'state': constants.STATUS_ERROR})
140 raise
142 self._remove_access_rules(
143 context, delete_rules, snapshot_instance['id'])
145 if self._check_needs_refresh(context, snapshot_instance['id']): 145 ↛ 146line 145 didn't jump to line 146 because the condition on line 145 was never true
146 self._update_access_rules(context, snapshot_instance,
147 share_server=share_server)
148 else:
149 LOG.info("Access rules were successfully applied for "
150 "snapshot instance: %s", snapshot_instance['id'])
152 def _check_needs_refresh(self, context, snapshot_instance_id):
154 rules = self.db.share_snapshot_access_get_all_for_snapshot_instance(
155 context, snapshot_instance_id)
157 return (any(rule['state'] in (
158 constants.ACCESS_STATE_QUEUED_TO_APPLY,
159 constants.ACCESS_STATE_QUEUED_TO_DENY)
160 for rule in rules))
162 def _remove_access_rules(self, context, rules, snapshot_instance_id):
163 if not rules:
164 return
166 for rule in rules:
167 self.db.share_snapshot_instance_access_delete(
168 context, rule['access_id'], snapshot_instance_id)
170 def get_snapshot_instance_access_rules(self, context,
171 snapshot_instance_id):
172 return self.db.share_snapshot_access_get_all_for_snapshot_instance(
173 context, snapshot_instance_id)