cinder-fusioncompute/cinder/volume/drivers/huawei/fusioncompute/volume_proxy.py

602 lines
23 KiB
Python

# Copyright 2016 Huawei Technologies Co.,LTD.
# 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.
"""
[VRM DRIVER] VRM CLIENT.
"""
import json
from cinder import exception as cinder_exception
from cinder.i18n import _
from cinder.volume.drivers.huawei.fusioncompute.base_proxy import BaseProxy
from cinder.volume.drivers.huawei.fusioncompute import exception as driver_exception
from cinder.volume.drivers.huawei.fusioncompute.task_proxy import TaskProxy
from oslo_log import log as logging
TASK_WAITING = 'waiting'
TASK_RUNNING = 'running'
TASK_SUCCESS = 'success'
TASK_FAILED = 'failed'
TASK_CANCELLING = 'cancelling'
TASK_UNKNOWN = 'unknown'
LOG = logging.getLogger(__name__)
class VolumeProxy(BaseProxy):
def __init__(self):
super(VolumeProxy, self).__init__()
self.task_proxy = TaskProxy()
def filter_not_none_dict(self, src_dic):
obj_dic = {}
for index_dic in src_dic:
if src_dic.get(index_dic) is not None:
obj_dic.update({index_dic: src_dic.get(index_dic)})
return obj_dic
def query_volume(self, **kwargs):
'''query_volume
'query_volume': ('GET',
('/volumes', kwargs.get(self.RESOURCE_URI),
None,
kwargs.get('id')),
{'limit': kwargs.get('limit'),
'offset': kwargs.get('offset'),
'scope': kwargs.get('scope')
},
{},
False),
'''
LOG.info(_("[VRM-CINDER] start query_volume()"))
uri = '/volumes'
method = 'GET'
path = self.site_uri + uri + '/' + kwargs.get('id')
new_url = self._generate_url(path)
resp, body = self.vrmhttpclient.request(new_url, method)
return body
def query_volume_replications(self, **kwargs):
'''query_volume_replications
'query_volume_replications': ('GET',
('/volumes/{volume_id}/action/replications',
kwargs.get(self.RESOURCE_URI), None,
kwargs.get('volume_id')),
False),
'''
LOG.info(_("[VRM-CINDER] start query_volume_replications()"))
uri = '/volumes'
method = 'GET'
path = self.site_uri + uri + '/' + kwargs.get(
'volume_id') + '/action/replications'
new_url = self._generate_url(path)
resp, body = self.vrmhttpclient.request(new_url, method)
return body
def list_volumes(self, **kwargs):
'''list_volumes
'list_volumes': ('GET',
('/volumes', kwargs.get(self.RESOURCE_URI),
None,
kwargs.get('id')),
{'limit': kwargs.get('limit'),
'offset': kwargs.get('offset'),
'scope': kwargs.get('scope')
},
{},
False),
'''
LOG.info(_("[VRM-CINDER] start query_volumesnapshot()"))
uri = '/volumes/compatibility/discovery '
method = 'GET'
path = self.site_uri + uri
offset = 0
volumes = []
volumes_map = {} # use map to clean repeat
while True:
parames = {
'limit': self.limit,
'offset': offset,
'scope': kwargs.get('scope'),
'uuid': kwargs.get('uuid')
}
appendix = self._joined_params(parames)
new_url = self._generate_url(path, appendix)
resp, body = self.vrmhttpclient.request(new_url, method)
total = int(body.get('total') or 0)
if total > 0:
res = body.get('volumes')
for volume in res:
volumes_map[volume.get('uuid')] = volume
volumes = volumes_map.values()
volumes += res
offset += len(res)
if offset >= total or len(volumes) >= total or len(
res) < self.limit:
break
if offset > 5:
offset -= 5
else:
break
for index_volume in volumes:
if index_volume.get(
'customProperties') is not None and index_volume.get(
'customProperties').get('external_uuid') is not None:
index_volume["uuid"] = index_volume["customProperties"][
"external_uuid"]
return volumes
def list_volumes_extend(self, **kwargs):
'''list_volumes_extend
'list_volumes_extend': ('GET',
('/volumes', kwargs.get(self.RESOURCE_URI),
None,
kwargs.get('id')),
{'limit': kwargs.get('limit'),
'offset': kwargs.get('offset'),
'scope': kwargs.get('scope')
},
{},
False),
'''
LOG.info(_("[VRM-CINDER] start query_volumesnapshot()"))
uri = '/volumes/extend'
method = 'GET'
path = self.site_uri + uri
offset = 0
volumes = []
volumes_map = {} # use map to clean repeat
while True:
parames = {
'limit': self.limit,
'offset': offset,
'scope': kwargs.get('scope'),
'uuid': kwargs.get('uuid')
}
appendix = self._joined_params(parames)
new_url = self._generate_url(path, appendix)
resp, body = self.vrmhttpclient.request(new_url, method)
total = int(body.get('total') or 0)
if total > 0:
res = body.get('volumes')
for volume in res:
volumes_map[volume.get('uuid')] = volume
volumes = volumes_map.values()
volumes += res
offset += len(res)
if offset >= total or len(volumes) >= total or len(
res) < self.limit:
break
if offset > 5:
offset -= 5
else:
break
for index_volume in volumes:
if index_volume.get(
'customProperties') is not None and index_volume.get(
'customProperties').get('external_uuid') is not None:
index_volume["uuid"] = index_volume["customProperties"][
"external_uuid"]
return volumes
def create_volume(self, **kwargs):
'''create_volume
'create_volume': ('POST',
('/volumes', None, None, None),
{},
{'name': kwargs.get('name'),
'quantityGB': kwargs.get('quantityGB'),
'datastoreUrn': kwargs.get('datastoreUrn'),
'uuid': kwargs.get('uuid'),
'isThin': kwargs.get('isThin'),
'type': kwargs.get('type'),
'indepDisk': kwargs.get('indepDisk'),
'persistentDisk':
kwargs.get('persistentDisk'),
'volumeId': kwargs.get('volumeId'),
'snapshotUuid': kwargs.get('snapshotUuid'),
'imageUrl': kwargs.get('imageUrl'),
},
True),
'''
LOG.info(_("[VRM-CINDER] start create_volume()"))
uri = '/volumes'
method = 'POST'
path = self.site_uri + uri
new_url = self._generate_url(path)
body = {
'name': kwargs.get('name'),
'quantityGB': kwargs.get('size'),
'datastoreUrn': kwargs.get('ds_urn'),
'uuid': kwargs.get('uuid'),
'isThin': kwargs.get('is_thin'),
'type': kwargs.get('type'),
'indepDisk': kwargs.get('independent'),
'customProperties': {"external_uuid": kwargs.get('uuid')}
}
support_pvscsi = kwargs.get('support_pvscsi', None)
if support_pvscsi is not None:
body.update({'pvscsiSupport': support_pvscsi})
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
return body
def delete_volume(self, **kwargs):
'''delete_volume
'delete_volume': ('DELETE',
('/volumes', kwargs.get(self.RESOURCE_URI),
None,
None),
{},
{},
True),
'''
LOG.info(_("[VRM-CINDER] start delete_volume()"))
method = 'DELETE'
path = kwargs.get('volume_uri')
new_url = self._generate_url(path)
error_busy = ['10300057', '10420005', '10420009', '10410154',
'10420137', '10420138', '10430058']
try:
resp, body = self.vrmhttpclient.request(new_url, method)
task_uri = body.get('taskUri')
self.task_proxy.wait_task(task_uri=task_uri)
except driver_exception.ClientException as ex:
LOG.info(_("[VRM-CINDER] delete volume (%s)"), ex.errorCode)
if ex.errorCode == "10420004":
return
elif ex.errorCode in error_busy:
LOG.info(_(
"[VRM-CINDER] volume status conflicts, try delete later."))
raise cinder_exception.VolumeIsBusy(message=ex.errorCode)
else:
raise ex
def clone_volume(self, **kwargs):
'''clone_volume
'clone_volume': ('POST',
('/volumes', None, kwargs.get('src_name'),'action/copyVol'),
{},
{'destinationVolumeID':
kwargs.get('dest_name')
},
True),
'''
LOG.info(_("[VRM-CINDER] start clone_volume()"))
uri = '/volumes'
method = 'POST'
path = self.site_uri + uri + '/' + kwargs.get(
'src_volume_id') + '/action/copyVol'
body = {'dstVolUrn': kwargs.get('dest_volume_urn')}
new_url = self._generate_url(path)
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
task_uri = body.get('taskUri')
self.task_proxy.wait_task(task_uri=task_uri)
def _copy_nfs_image_to_volume(self, **kwargs):
'''_copy_nfs_image_to_volume
'copy_image_to_volume': ('POST',
('/volumes/imagetovolume', None, None, None),
{},
{
'volumePara': {
'quantityGB':
kwargs.get('volume_size'),
'urn': kwargs.get('volume_urn')
},
'imagePara': {
'id': kwargs.get('image_id'),
'url': kwargs.get('image_location')
},
'location': kwargs.get('cluster_urn'),
'needCreateVolume': False
},
True),
'''
LOG.info(_("[VRM-CINDER] start copy_image_to_volume()"))
uri = '/volumes/imagetovolume'
method = 'POST'
path = self.site_uri + uri
new_url = self._generate_url(path)
body = {
'volumePara': {
'quantityGB': kwargs.get('volume_size'),
'urn': kwargs.get('volume_urn')
},
'imagePara': {
'id': kwargs.get('image_id'),
'url': kwargs.get('image_location')
},
'location': kwargs.get('cluster_urn'),
'needCreateVolume': False
}
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
task_uri = body.get('taskUri')
self.task_proxy.wait_task(task_uri=task_uri)
def _copy_volume_to_image(self, **kwargs):
'''_copy_volume_to_image
'copy_volume_to_image': ('POST',
('/volumes/volumetoimage', None, None, None),
{},
{
'volumePara': {'urn':
kwargs.get('volume_urn'),
'quantityGB':
kwargs.get('volume_size')},
'imagePara': {
'id': kwargs.get(
'image_id'),
'url': kwargs.get(
'image_url')}
},
True),
'''
LOG.info(_("[VRM-CINDER] start stop_vm()"))
uri = '/volumes/volumetoimage'
method = 'POST'
path = self.site_uri + uri
new_url = self._generate_url(path)
body = {
'volumePara': {
'urn': kwargs.get('volume_urn'),
'quantityGB': kwargs.get('volume_size')},
'imagePara': {
'id': kwargs.get('image_id'),
'url': kwargs.get('image_url')}
}
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
task_uri = body.get('taskUri')
self.task_proxy.wait_task(task_uri=task_uri)
def manage_existing(self, **kwargs):
'''manage_existing
'manage_existing': ('POST',
('/volumes', None, None, None),
{},
{'name': kwargs.get('name'),
'quantityGB': kwargs.get('quantityGB'),
'datastoreUrn': kwargs.get('datastoreUrn'),
'uuid': kwargs.get('uuid'),
'type': kwargs.get('type'),
'indepDisk': kwargs.get('indepDisk'),
'persistentDisk':
kwargs.get('persistentDisk'),
'volumeId': kwargs.get('volumeId'),
'snapshotUuid': kwargs.get('snapshotUuid'),
'imageUrl': kwargs.get('imageUrl'),
},
True),
'''
LOG.info(_("[VRM-CINDER] start create_volume()"))
uri = '/volumes/registevol'
method = 'POST'
path = self.site_uri + uri
new_url = self._generate_url(path)
body = {
'name': kwargs.get('name'),
'quantityGB': kwargs.get('quantityGB'),
'volInfoUrl': kwargs.get('volInfoUrl'),
'uuid': kwargs.get('uuid'),
'customProperties': {"external_uuid": kwargs.get('uuid')},
'type': kwargs.get('type'),
'maxReadBytes': kwargs.get('maxReadBytes'),
'maxWriteBytes': kwargs.get('maxWriteBytes'),
'maxReadRequest': kwargs.get('maxReadRequest'),
'maxWriteRequest': kwargs.get('maxWriteRequest')}
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
return body
def unmanage(self, **kwargs):
'''unmanage
'unmanage': ('DELETE',
('/volumes?isOnlyDelDB=1',
kwargs.get(self.RESOURCE_URI), None, None),
{},
{},
True),
'''
LOG.info(_("[VRM-CINDER] start unmanage()"))
method = 'DELETE'
path = kwargs.get('volume_uri') + '?isOnlyDelDB=1'
new_url = self._generate_url(path)
try:
resp, body = self.vrmhttpclient.request(new_url, method)
task_uri = body.get('taskUri')
self.task_proxy.wait_task(task_uri=task_uri)
except driver_exception.ClientException as ex:
LOG.info(_("[VRM-CINDER] unmanage volume (%s)"), ex.errorCode)
if ex.errorCode == "10420004":
return
else:
raise ex
def migrate_volume(self, **kwargs):
'''migrate_volume
Post <vol_uri>/<volid>/action/migratevol HTTP/1.1
Host https://<ip>:<port>
Accept application/json;version=<version>; charset=UTF-8
X-Auth-Token: <Authen_TOKEN>
{
'datastoreUrn':string,
'speed': integer
}
'''
LOG.info(_("[VRM-CINDER] start migrate_volume()"))
uri = '/volumes'
method = 'POST'
path = self.site_uri + uri + '/' + kwargs.get(
'volume_id') + '/action/migratevol'
body = {'datastoreUrn': kwargs.get('dest_ds_urn'),
'speed': kwargs.get('speed'),
'migrateType': kwargs.get('migrate_type')}
new_url = self._generate_url(path)
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
task_uri = body.get('taskUri')
self.task_proxy.wait_task(task_uri=task_uri)
def modify_volume(self, **kwargs):
LOG.info(_("[VRM-CINDER] start modify_volume()"))
uri = '/volumes'
method = 'PUT'
path = self.site_uri + uri + '/' + kwargs.get('volume_id')
body = {}
if kwargs.get('type') is not None:
body.update({'type': kwargs.get('type')})
if kwargs.get('name') is not None:
body.update({'name': kwargs.get('name')})
new_url = self._generate_url(path)
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
if resp.status_code not in (200, 204):
raise driver_exception.ClientException(101)
def extend_volume(self, **kwargs):
'''extend_volume
'extend_volume': ('POST',
(kwargs.get('volume_uri'),'/action/expandVol',
None, None),
{},
{},
True),
'''
LOG.info(_("[VRM-CINDER] start extend_volume()"))
method = 'POST'
body = {'size': kwargs.get('size')}
volume_uri = kwargs.get('volume_uri')
path = volume_uri + '/action/expandVol'
new_url = self._generate_url(path)
try:
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
task_uri = body.get('taskUri')
self.task_proxy.wait_task(task_uri=task_uri)
except driver_exception.ClientException as ex:
LOG.info(_("[VRM-CINDER] extend volume (%s)"), ex.errorCode)
raise ex
except Exception as ex:
LOG.info(_("[VRM-CINDER] extend volume (%s)"), ex)
raise ex
def wait_task(self, **kwargs):
task_uri = kwargs.get('taskUri')
self.task_proxy.wait_task(task_uri=task_uri)
def query_volume_by_custom_properties(self, **kwargs):
'''query_volume_by_custom_properties
'query_volume_by_custom_properties': ('POST',
('/volumes/queryby/custom-properties', None, None,
None),
{},
{"condition": {"name": "external_uuid",
"value": "93DF26A9-2D3B-43A6-A7D8-CF5E9D0DC17C"
},
"offset": 0,
limit": 10
},
True),
'''
LOG.info(_("[VRM-CINDER] start query_volume_by_custom_properties()"))
uri = '/volumes/queryby/custom-properties'
method = 'POST'
path = self.site_uri + uri
new_url = self._generate_url(path)
body = {
'condition': {
'name': 'external_uuid',
'value': kwargs.get('external_uuid')
}
}
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
volumes = []
total = int(body.get('total') or 0)
if total > 0:
res = body.get('volumes')
volumes += res
return volumes[0]
return None
def update_custom_properties(self, **kwargs):
'''update_custom_properties
'update_by_custom_properties': ('POST',
('/volumes/properties/custom', None, None, None),
{},
{"volumes": [
'volumeUrn': kwargs.get('volumeUrn'),
'customProperties': {
'external_uuid': kwargs.get('external_uuid')
}]
},
True),
'''
LOG.info(_("[VRM-CINDER] start modify_custom_properties()"))
uri = '/volumes/properties/custom'
method = 'POST'
path = self.site_uri + uri
new_url = self._generate_url(path)
body = {
'volumes': [
{
'volumeUrn': kwargs.get('volume_urn'),
'customProperties': {
'external_uuid': kwargs.get('external_uuid')
}
}
]
}
resp, body = self.vrmhttpclient.request(new_url, method,
body=json.dumps(body))
return body