a203f363d9
This patch moves the zfssa exception to the zfssa webdavclient Change-Id: Idbd9f2f5befcf89e34f668a6620e7fe3e05a35da
161 lines
6.0 KiB
Python
161 lines
6.0 KiB
Python
# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
"""
|
|
ZFS Storage Appliance WebDAV Client
|
|
"""
|
|
|
|
import time
|
|
|
|
from oslo_log import log
|
|
from six.moves import http_client
|
|
from six.moves import urllib
|
|
|
|
from cinder import exception
|
|
from cinder.i18n import _
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
bad_gateway_err = _('Check the state of the http service. Also ensure that '
|
|
'the https port number is the same as the one specified '
|
|
'in cinder.conf.')
|
|
|
|
WebDAVHTTPErrors = {
|
|
http_client.UNAUTHORIZED: _('User not authorized to perform WebDAV '
|
|
'operations.'),
|
|
http_client.BAD_GATEWAY: bad_gateway_err,
|
|
http_client.FORBIDDEN: _('Check access permissions for the ZFS share '
|
|
'assigned to this driver.'),
|
|
http_client.NOT_FOUND: _('The source volume for this WebDAV operation not '
|
|
'found.'),
|
|
http_client.INSUFFICIENT_STORAGE: _('Not enough storage space in the ZFS '
|
|
'share to perform this operation.')
|
|
}
|
|
|
|
WebDAVErrors = {
|
|
'BadStatusLine': _('http service may have been abruptly disabled or put '
|
|
'to maintenance state in the middle of this '
|
|
'operation.'),
|
|
'Bad_Gateway': bad_gateway_err
|
|
}
|
|
|
|
propertyupdate_data = """<?xml version="1.0"?>
|
|
<D:propertyupdate xmlns:D="DAV:">
|
|
<D:set>
|
|
<D:prop>
|
|
<D:prop_name>prop_val</D:prop_name>
|
|
</D:prop>
|
|
</D:set>
|
|
</D:propertyupdate>"""
|
|
|
|
|
|
class WebDAVClientError(exception.VolumeDriverException):
|
|
message = _("The WebDAV request failed. Reason: %(msg)s, "
|
|
"Return code/reason: %(code)s, Source Volume: %(src)s, "
|
|
"Destination Volume: %(dst)s, Method: %(method)s.")
|
|
|
|
|
|
class ZFSSAWebDAVClient(object):
|
|
def __init__(self, url, auth_str, **kwargs):
|
|
"""Initialize WebDAV Client"""
|
|
self.https_path = url
|
|
self.auth_str = auth_str
|
|
|
|
def _lookup_error(self, error):
|
|
msg = ''
|
|
if error in http_client.responses:
|
|
msg = http_client.responses[error]
|
|
|
|
if error in WebDAVHTTPErrors:
|
|
msg = WebDAVHTTPErrors[error]
|
|
elif error in WebDAVErrors:
|
|
msg = WebDAVErrors[error]
|
|
|
|
return msg
|
|
|
|
def build_data(self, data, propname, value):
|
|
res = data.replace('prop_name', propname)
|
|
res = res.replace('prop_val', value)
|
|
return res
|
|
|
|
def set_file_prop(self, filename, propname, propval):
|
|
data = self.build_data(propertyupdate_data, propname, propval)
|
|
return self.request(src_file=filename, data=data, method='PROPPATCH')
|
|
|
|
def request(self, src_file="", dst_file="", method="", maxretries=10,
|
|
data=""):
|
|
retry = 0
|
|
src_url = self.https_path + "/" + src_file
|
|
dst_url = self.https_path + "/" + dst_file
|
|
request = urllib.request.Request(url=src_url, data=data)
|
|
|
|
if dst_file != "":
|
|
request.add_header('Destination', dst_url)
|
|
if method == "PROPPATCH":
|
|
request.add_header('Translate', 'F')
|
|
|
|
request.add_header("Authorization", "Basic %s" % self.auth_str)
|
|
|
|
request.get_method = lambda: method
|
|
|
|
LOG.debug('Sending WebDAV request:%(method)s %(src)s %(des)s',
|
|
{'method': method, 'src': src_url, 'des': dst_url})
|
|
|
|
while retry < maxretries:
|
|
try:
|
|
# URLs are prepended with self.https_path which is safe
|
|
# meaning that the URL will either be safe or nonexistant
|
|
response = urllib.request.urlopen( # nosec
|
|
request, timeout=None)
|
|
except urllib.error.HTTPError as err:
|
|
LOG.error('WebDAV returned with %(code)s error during '
|
|
'%(method)s call.',
|
|
{'code': err.code, 'method': method})
|
|
|
|
if err.code == http_client.INTERNAL_SERVER_ERROR:
|
|
LOG.error('WebDAV operation failed with error code: '
|
|
'%(code)s reason: %(reason)s Retry attempt '
|
|
'%(retry)s in progress.',
|
|
{'code': err.code,
|
|
'reason': err.reason,
|
|
'retry': retry})
|
|
if retry < maxretries:
|
|
retry += 1
|
|
time.sleep(1)
|
|
continue
|
|
|
|
msg = self._lookup_error(err.code)
|
|
raise WebDAVClientError(msg=msg, code=err.code,
|
|
src=src_file, dst=dst_file,
|
|
method=method)
|
|
|
|
except http_client.BadStatusLine as err:
|
|
msg = self._lookup_error('BadStatusLine')
|
|
code = 'http_client.BadStatusLine'
|
|
raise WebDAVClientError(msg=msg, code=code,
|
|
src=src_file, dst=dst_file,
|
|
method=method)
|
|
|
|
except urllib.error.URLError as err:
|
|
reason = ''
|
|
if getattr(err, 'reason'):
|
|
reason = err.reason
|
|
|
|
msg = self._lookup_error('Bad_Gateway')
|
|
raise WebDAVClientError(msg=msg, code=reason,
|
|
src=src_file,
|
|
dst=dst_file, method=method)
|
|
|
|
break
|
|
return response
|