Coverage for manila/share/drivers/glusterfs/glusterfs_native.py: 99%

75 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright (c) 2014 Red Hat, Inc. 

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 

16""" GlusterFS native protocol (glusterfs) driver for shares. 

17 

18Manila share is a GlusterFS volume. Unlike the generic driver, this 

19does not use service VM approach. Instances directly talk with the 

20GlusterFS backend storage pool. Instance use the 'glusterfs' protocol 

21to mount the GlusterFS share. Access to the share is allowed via 

22SSL Certificates. Only the instance which has the SSL trust established 

23with the GlusterFS backend can mount and hence use the share. 

24 

25Supports working with multiple glusterfs volumes. 

26""" 

27 

28import re 

29 

30from oslo_log import log 

31 

32from manila.common import constants 

33from manila import exception 

34from manila.i18n import _ 

35from manila.share import driver 

36from manila.share.drivers.glusterfs import common 

37from manila.share.drivers.glusterfs import layout 

38from manila import utils 

39 

40LOG = log.getLogger(__name__) 

41 

42 

43ACCESS_TYPE_CERT = 'cert' 

44AUTH_SSL_ALLOW = 'auth.ssl-allow' 

45CLIENT_SSL = 'client.ssl' 

46NFS_EXPORT_VOL = 'nfs.export-volumes' 

47SERVER_SSL = 'server.ssl' 

48DYNAMIC_AUTH = 'server.dynamic-auth' 

49 

50 

51class GlusterfsNativeShareDriver(driver.ExecuteMixin, 

52 layout.GlusterfsShareDriverBase): 

53 """GlusterFS native protocol (glusterfs) share driver. 

54 

55 Executes commands relating to Shares. 

56 Supports working with multiple glusterfs volumes. 

57 

58 API version history: 

59 

60 1.0 - Initial version. 

61 1.1 - Support for working with multiple gluster volumes. 

62 """ 

63 

64 GLUSTERFS_VERSION_MIN = (3, 6) 

65 

66 _supported_access_levels = (constants.ACCESS_LEVEL_RW, ) 

67 _supported_access_types = (ACCESS_TYPE_CERT, ) 

68 supported_layouts = ('layout_volume.GlusterfsVolumeMappedLayout',) 

69 supported_protocols = ('GLUSTERFS',) 

70 

71 def __init__(self, *args, **kwargs): 

72 super(GlusterfsNativeShareDriver, self).__init__( 

73 False, *args, **kwargs) 

74 LOG.warning('GlusterFS native share driver has been deprecated and is ' 

75 'expected to be removed in a future release.') 

76 self._helpers = None 

77 self.backend_name = self.configuration.safe_get( 

78 'share_backend_name') or 'GlusterFS-Native' 

79 

80 def _setup_via_manager(self, share_mgr, share_mgr_parent=None): 

81 # Enable gluster volumes for SSL access only. 

82 

83 gluster_mgr = share_mgr['manager'] 

84 gluster_mgr_parent = (share_mgr_parent or {}).get('manager', None) 

85 

86 ssl_allow_opt = (gluster_mgr_parent if gluster_mgr_parent else 

87 gluster_mgr).get_vol_option( 

88 AUTH_SSL_ALLOW) 

89 if not ssl_allow_opt: 

90 # Not having AUTH_SSL_ALLOW set is a problematic edge case. 

91 # - In GlusterFS 3.6, it implies that access is allowed to 

92 # none, including intra-service access, which causes 

93 # problems internally in GlusterFS 

94 # - In GlusterFS 3.7, it implies that access control is 

95 # disabled, which defeats the purpose of this driver -- 

96 # so to avoid these possibilities, we throw an error in this case. 

97 msg = (_("Option %(option)s is not defined on gluster volume. " 

98 "Volume: %(volname)s") % 

99 {'volname': gluster_mgr.volume, 

100 'option': AUTH_SSL_ALLOW}) 

101 LOG.error(msg) 

102 raise exception.GlusterfsException(msg) 

103 

104 gluster_actions = [] 

105 if gluster_mgr_parent: 

106 # The clone of the snapshot, a new volume, retains the authorized 

107 # access list of the snapshotted volume/share, which includes TLS 

108 # identities of the backend servers, Manila hosts and clients. 

109 # Retain the identities of the GlusterFS servers and Manila host, 

110 # and exclude those of the clients in the authorized access list of 

111 # the new volume. The TLS identities of GlusterFS servers are 

112 # determined as those that are prefixed by 'glusterfs-server'. 

113 # And the TLS identity of the Manila host is identified as the 

114 # one that has 'manila-host' as the prefix. 

115 # Wrt. GlusterFS' parsing of auth.ssl-allow, please see code from 

116 # https://github.com/gluster/glusterfs/blob/v3.6.2/ 

117 # xlators/protocol/auth/login/src/login.c#L80 

118 # until end of gf_auth() function 

119 old_access_list = re.split('[ ,]', ssl_allow_opt) 

120 glusterfs_server_CN_pattern = r'\Aglusterfs-server' 

121 manila_host_CN_pattern = r'\Amanila-host' 

122 regex = re.compile( 

123 r'%(pattern1)s|%(pattern2)s' % { 

124 'pattern1': glusterfs_server_CN_pattern, 

125 'pattern2': manila_host_CN_pattern}) 

126 access_to = ','.join(filter(regex.match, old_access_list)) 

127 gluster_actions.append((AUTH_SSL_ALLOW, access_to)) 

128 

129 for option, value in ( 

130 (NFS_EXPORT_VOL, False), (CLIENT_SSL, True), (SERVER_SSL, True) 

131 ): 

132 gluster_actions.append((option, value)) 

133 

134 for action in gluster_actions: 

135 gluster_mgr.set_vol_option(*action) 

136 

137 gluster_mgr.set_vol_option(DYNAMIC_AUTH, True, ignore_failure=True) 

138 

139 # SSL enablement requires a fresh volume start 

140 # to take effect 

141 if gluster_mgr_parent: 

142 # in this case the volume is not started 

143 # yet (will only be started after this func 

144 # returns), so we have nothing to do here 

145 pass 

146 else: 

147 common._restart_gluster_vol(gluster_mgr) 

148 

149 return gluster_mgr.export 

150 

151 @utils.synchronized("glusterfs_native_access", external=False) 

152 def _update_access_via_manager(self, gluster_mgr, context, share, 

153 add_rules, delete_rules, 

154 recovery=False, share_server=None): 

155 """Update access rules, authorize SSL CNs (Common Names).""" 

156 

157 # Fetch existing authorized CNs, the value of Gluster option 

158 # 'auth.ssl-allow' that is available as a comma separated string. 

159 # wrt. GlusterFS' parsing of auth.ssl-allow, please see code from 

160 # https://github.com/gluster/glusterfs/blob/v3.6.2/ 

161 # xlators/protocol/auth/login/src/login.c#L80 

162 # until end of gf_auth() function 

163 ssl_allow_opt = gluster_mgr.get_vol_option(AUTH_SSL_ALLOW) 

164 

165 existing_rules_set = set(re.split('[ ,]', ssl_allow_opt)) 

166 add_rules_set = {rule['access_to'] for rule in add_rules} 

167 for rule in add_rules_set: 

168 if re.search('[ ,]', rule): 

169 raise exception.GlusterfsException( 

170 _("Invalid 'access_to' '%s': common names used for " 

171 "GlusterFS authentication should not contain comma " 

172 "or whitespace.") % rule) 

173 delete_rules_set = {rule['access_to'] for rule in delete_rules} 

174 new_rules_set = ( 

175 (existing_rules_set | add_rules_set) - delete_rules_set) 

176 

177 # Addition or removal of CNs in the authorized list through the 

178 # Gluster CLI, used by 'GlusterManager' objects, can only be done by 

179 # replacing the existing list with the newly modified list. 

180 ssl_allow_opt = ','.join(sorted(new_rules_set)) 

181 gluster_mgr.set_vol_option(AUTH_SSL_ALLOW, ssl_allow_opt) 

182 

183 # When the Gluster option, DYNAMIC_AUTH is not enabled for the gluster 

184 # volume/manila share, the removal of CN of a client does not affect 

185 # the client's existing connection to the volume until the volume is 

186 # restarted. 

187 if delete_rules: 187 ↛ exitline 187 didn't return from function '_update_access_via_manager' because the condition on line 187 was always true

188 dynauth = gluster_mgr.get_vol_option(DYNAMIC_AUTH, boolean=True) 

189 if not dynauth: 

190 common._restart_gluster_vol(gluster_mgr) 

191 

192 def _update_share_stats(self): 

193 """Send stats info for the GlusterFS volume.""" 

194 

195 data = dict( 

196 share_backend_name=self.backend_name, 

197 vendor_name='Red Hat', 

198 driver_version='1.1', 

199 storage_protocol='glusterfs', 

200 reserved_percentage=self.configuration.reserved_share_percentage, 

201 reserved_snapshot_percentage=( 

202 self.configuration.reserved_share_from_snapshot_percentage or 

203 self.configuration.reserved_share_percentage), 

204 reserved_share_extend_percentage=( 

205 self.configuration.reserved_share_extend_percentage or 

206 self.configuration.reserved_share_percentage)) 

207 

208 # We don't use a service mount to get stats data. 

209 # Instead we use glusterfs quota feature and use that to limit 

210 # the share to its expected share['size']. 

211 

212 # TODO(deepakcs): Change below once glusterfs supports volume 

213 # specific stats via the gluster cli. 

214 data['total_capacity_gb'] = 'unknown' 

215 data['free_capacity_gb'] = 'unknown' 

216 

217 super(GlusterfsNativeShareDriver, self)._update_share_stats(data) 

218 

219 def get_network_allocations_number(self): 

220 return 0