Coverage for manila/share/drivers/hitachi/hsp/rest.py: 98%

112 statements  

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

1# Copyright (c) 2016 Hitachi Data Systems, 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 

16import json 

17import requests 

18 

19from manila import exception 

20from manila.i18n import _ 

21from manila import utils 

22 

23 

24# Suppress the Insecure request warnings 

25requests.packages.urllib3.disable_warnings() # pylint: disable=no-member 

26 

27 

28class HSPRestBackend(object): 

29 def __init__(self, hsp_host, hsp_username, hsp_password): 

30 self.host = hsp_host 

31 self.username = hsp_username 

32 self.password = hsp_password 

33 

34 def _send_post(self, url, payload=None): 

35 resp = requests.post(url, auth=(self.username, self.password), 

36 data=payload, verify=False) 

37 

38 if resp.status_code == 202: 

39 self._wait_job_status(resp.headers['location'], 'COMPLETE') 

40 else: 

41 msg = (_("HSP API post failed: %s.") % 

42 resp.json()['messages'][0]['message']) 

43 raise exception.HSPBackendException(msg=msg) 

44 

45 def _send_get(self, url, payload=None): 

46 resp = requests.get(url, auth=(self.username, self.password), 

47 data=payload, verify=False) 

48 

49 if resp.status_code == 200: 

50 if resp.content == 'null': 

51 return None 

52 else: 

53 return resp.json() 

54 else: 

55 msg = (_("HSP API get failed: %s.") % 

56 resp.json()['messages'][0]['message']) 

57 raise exception.HSPBackendException(msg=msg) 

58 

59 def _send_delete(self, url, payload=None): 

60 resp = requests.delete(url, auth=(self.username, self.password), 

61 data=payload, verify=False) 

62 

63 if resp.status_code == 202: 

64 self._wait_job_status(resp.headers['location'], 'COMPLETE') 

65 else: 

66 msg = (_("HSP API delete failed: %s.") % 

67 resp.json()['messages'][0]['message']) 

68 raise exception.HSPBackendException(msg=msg) 

69 

70 def add_file_system(self, name, quota): 

71 url = "https://%s/hspapi/file-systems/" % self.host 

72 payload = { 

73 'quota': quota, 

74 'auto-access': False, 

75 'enabled': True, 

76 'description': '', 

77 'record-access-time': True, 

78 'tags': '', 

79 # Usage percentage in which a warning will be shown 

80 'space-hwm': 90, 

81 # Usage percentage in which the warning will be cleared 

82 'space-lwm': 70, 

83 'name': name, 

84 } 

85 self._send_post(url, payload=json.dumps(payload)) 

86 

87 def get_file_system(self, name): 

88 url = ("https://%s/hspapi/file-systems/list?name=%s" % 

89 (self.host, name)) 

90 

91 filesystems = self._send_get(url) 

92 

93 try: 

94 return filesystems['list'][0] 

95 except (TypeError, KeyError, IndexError): 

96 msg = _("Filesystem does not exist or is not available.") 

97 raise exception.HSPItemNotFoundException(msg=msg) 

98 

99 def delete_file_system(self, filesystem_id): 

100 url = "https://%s/hspapi/file-systems/%s" % (self.host, filesystem_id) 

101 self._send_delete(url) 

102 

103 def resize_file_system(self, filesystem_id, new_size): 

104 url = "https://%s/hspapi/file-systems/%s" % (self.host, filesystem_id) 

105 payload = {'quota': new_size} 

106 

107 self._send_post(url, payload=json.dumps(payload)) 

108 

109 def rename_file_system(self, filesystem_id, new_name): 

110 url = "https://%s/hspapi/file-systems/%s" % (self.host, filesystem_id) 

111 payload = {'name': new_name} 

112 

113 self._send_post(url, payload=json.dumps(payload)) 

114 

115 def add_share(self, name, filesystem_id): 

116 url = "https://%s/hspapi/shares/" % self.host 

117 payload = { 

118 'description': '', 

119 'type': 'NFS', 

120 'enabled': True, 

121 'tags': '', 

122 'name': name, 

123 'file-system-id': filesystem_id, 

124 } 

125 

126 self._send_post(url, payload=json.dumps(payload)) 

127 

128 def get_share(self, fs_id=None, name=None): 

129 if fs_id is not None: 

130 url = ('https://%s/hspapi/shares/list?file-system-id=%s' % 

131 (self.host, fs_id)) 

132 elif name is not None: 132 ↛ 135line 132 didn't jump to line 135 because the condition on line 132 was always true

133 url = ('https://%s/hspapi/shares/list?name=%s' % 

134 (self.host, name)) 

135 share = self._send_get(url) 

136 

137 try: 

138 return share['list'][0] 

139 except (TypeError, KeyError, IndexError): 

140 msg = _("Share %s does not exist or is not available.") 

141 

142 if fs_id is not None: 142 ↛ 143line 142 didn't jump to line 143 because the condition on line 142 was never true

143 args = "for filesystem %s" % fs_id 

144 else: 

145 args = name 

146 

147 raise exception.HSPItemNotFoundException(msg=msg % args) 

148 

149 def delete_share(self, share_id): 

150 url = "https://%s/hspapi/shares/%s" % (self.host, share_id) 

151 self._send_delete(url) 

152 

153 def add_access_rule(self, share_id, host_to, read_write): 

154 url = "https://%s/hspapi/shares/%s/" % (self.host, share_id) 

155 payload = { 

156 "action": "add-access-rule", 

157 "name": share_id + host_to, 

158 "host-specification": host_to, 

159 "read-write": read_write, 

160 } 

161 

162 self._send_post(url, payload=json.dumps(payload)) 

163 

164 def delete_access_rule(self, share_id, rule_name): 

165 url = "https://%s/hspapi/shares/%s/" % (self.host, share_id) 

166 payload = { 

167 "action": "delete-access-rule", 

168 "name": rule_name, 

169 } 

170 

171 self._send_post(url, payload=json.dumps(payload)) 

172 

173 def get_access_rules(self, share_id): 

174 url = ("https://%s/hspapi/shares/%s/access-rules" % 

175 (self.host, share_id)) 

176 rules = self._send_get(url) 

177 

178 try: 

179 rules = rules['list'] 

180 except (TypeError, KeyError, IndexError): 

181 rules = [] 

182 return rules 

183 

184 def get_cluster(self): 

185 url = "https://%s/hspapi/clusters/list" % self.host 

186 clusters = self._send_get(url) 

187 

188 try: 

189 return clusters['list'][0] 

190 except (TypeError, KeyError, IndexError): 

191 msg = _("No cluster was found on HSP.") 

192 raise exception.HSPBackendException(msg=msg) 

193 

194 @utils.retry(retry_param=exception.HSPTimeoutException, 

195 retries=10, 

196 wait_random=True) 

197 def _wait_job_status(self, job_url, target_status): 

198 resp_json = self._send_get(job_url) 

199 

200 status = resp_json['properties']['completion-status'] 

201 

202 if status == 'ERROR': 

203 msg = _("HSP job %(id)s failed. %(reason)s") 

204 job_id = resp_json['id'] 

205 reason = resp_json['properties']['completion-details'] 

206 raise exception.HSPBackendException(msg=msg % {'id': job_id, 

207 'reason': reason}) 

208 elif status != target_status: 

209 msg = _("Timeout while waiting for job %s to complete.") 

210 args = resp_json['id'] 

211 raise exception.HSPTimeoutException(msg=msg % args)