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

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. 

15 

16from oslo_log import log 

17 

18from manila.common import constants 

19from manila import utils 

20 

21LOG = log.getLogger(__name__) 

22 

23 

24class ShareSnapshotInstanceAccess(object): 

25 

26 def __init__(self, db, driver): 

27 self.db = db 

28 self.driver = driver 

29 

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. 

33 

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'] 

42 

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) 

47 

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 ) 

54 

55 def _update_access_rules(self, context, snapshot_instance, 

56 delete_all_rules=None, share_server=None): 

57 

58 # NOTE(ganso): First let's get all the rules and the mappings. 

59 

60 rules = self.db.share_snapshot_access_get_all_for_snapshot_instance( 

61 context, snapshot_instance['id']) 

62 

63 add_rules = [] 

64 delete_rules = [] 

65 

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}) 

77 

78 else: 

79 

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 ] 

93 

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) 

117 

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) 

126 

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}) 

132 

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 

141 

142 self._remove_access_rules( 

143 context, delete_rules, snapshot_instance['id']) 

144 

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']) 

151 

152 def _check_needs_refresh(self, context, snapshot_instance_id): 

153 

154 rules = self.db.share_snapshot_access_get_all_for_snapshot_instance( 

155 context, snapshot_instance_id) 

156 

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)) 

161 

162 def _remove_access_rules(self, context, rules, snapshot_instance_id): 

163 if not rules: 

164 return 

165 

166 for rule in rules: 

167 self.db.share_snapshot_instance_access_delete( 

168 context, rule['access_id'], snapshot_instance_id) 

169 

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)