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
« 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.
16import json
17import requests
19from manila import exception
20from manila.i18n import _
21from manila import utils
24# Suppress the Insecure request warnings
25requests.packages.urllib3.disable_warnings() # pylint: disable=no-member
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
34 def _send_post(self, url, payload=None):
35 resp = requests.post(url, auth=(self.username, self.password),
36 data=payload, verify=False)
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)
45 def _send_get(self, url, payload=None):
46 resp = requests.get(url, auth=(self.username, self.password),
47 data=payload, verify=False)
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)
59 def _send_delete(self, url, payload=None):
60 resp = requests.delete(url, auth=(self.username, self.password),
61 data=payload, verify=False)
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)
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))
87 def get_file_system(self, name):
88 url = ("https://%s/hspapi/file-systems/list?name=%s" %
89 (self.host, name))
91 filesystems = self._send_get(url)
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)
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)
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}
107 self._send_post(url, payload=json.dumps(payload))
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}
113 self._send_post(url, payload=json.dumps(payload))
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 }
126 self._send_post(url, payload=json.dumps(payload))
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)
137 try:
138 return share['list'][0]
139 except (TypeError, KeyError, IndexError):
140 msg = _("Share %s does not exist or is not available.")
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
147 raise exception.HSPItemNotFoundException(msg=msg % args)
149 def delete_share(self, share_id):
150 url = "https://%s/hspapi/shares/%s" % (self.host, share_id)
151 self._send_delete(url)
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 }
162 self._send_post(url, payload=json.dumps(payload))
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 }
171 self._send_post(url, payload=json.dumps(payload))
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)
178 try:
179 rules = rules['list']
180 except (TypeError, KeyError, IndexError):
181 rules = []
182 return rules
184 def get_cluster(self):
185 url = "https://%s/hspapi/clusters/list" % self.host
186 clusters = self._send_get(url)
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)
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)
200 status = resp_json['properties']['completion-status']
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)