ecfb70cfeb
It will support the minimum set of features required in Cinder: - Volume Create/Delete - Volume Attach/Detach - Snapshot Create/Delete - Create Volume from Snapshot - Get Volume Stats - Copy Image to Volume - Copy Volume to Image - Clone Volume - Extend Volume DocImpact Implements: bp fusionstorage-cinder-driver Co-Authored-By: wangxiyuan <wangxiyuan@huawei.com> Change-Id: I26a809adb7bef4370eb53c634dc9bae0c74b4a8a
500 lines
18 KiB
Python
500 lines
18 KiB
Python
# Copyright (c) 2013 - 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.
|
|
"""
|
|
Volume api for FusionStorage systems.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import six
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from cinder.i18n import _LE
|
|
from cinder import utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
fsc_conf_file = "/etc/cinder/volumes/fsc_conf"
|
|
fsc_cli = "fsc_cli"
|
|
fsc_ip = []
|
|
fsc_port = '10519'
|
|
manage_ip = "127.0.0.1"
|
|
CMD_BIN = fsc_cli
|
|
|
|
volume_info = {
|
|
'result': '',
|
|
'vol_name': '',
|
|
'father_name': '',
|
|
'status': '',
|
|
'vol_size': '',
|
|
'real_size': '',
|
|
'pool_id': '',
|
|
'create_time': ''}
|
|
|
|
|
|
snap_info = {
|
|
'result': '',
|
|
'snap_name': '',
|
|
'father_name': '',
|
|
'status': '',
|
|
'snap_size': '',
|
|
'real_size': '',
|
|
'pool_id': '',
|
|
'delete_priority': '',
|
|
'create_time': ''}
|
|
|
|
|
|
pool_info = {
|
|
'result': '',
|
|
'pool_id': '',
|
|
'total_capacity': '',
|
|
'used_capacity': '',
|
|
'alloc_capacity': ''}
|
|
|
|
|
|
class FSPythonApi(object):
|
|
|
|
def __init__(self):
|
|
LOG.debug("FSPythonApi init.")
|
|
self.get_ip_port()
|
|
self.res_idx = len('result=')
|
|
|
|
def get_ip_port(self):
|
|
LOG.debug("File fsc_conf_file is %s.", fsc_conf_file)
|
|
if os.path.exists(fsc_conf_file):
|
|
try:
|
|
fsc_file = open(fsc_conf_file, 'r')
|
|
full_txt = fsc_file.readlines()
|
|
LOG.debug("Full_txt is %s.", full_txt)
|
|
for line in full_txt:
|
|
if re.search('^vbs_url=', line):
|
|
tmp_vbs_url = line[8:]
|
|
return re.split(',', tmp_vbs_url)
|
|
except Exception as e:
|
|
LOG.debug("Get fsc ip failed, error=%s.", e)
|
|
finally:
|
|
fsc_file.close()
|
|
else:
|
|
LOG.debug("Fsc conf file not exist, file_name=%s.", fsc_conf_file)
|
|
|
|
def get_manage_ip(self):
|
|
LOG.debug("File fsc_conf_file is %s.", fsc_conf_file)
|
|
if os.path.exists(fsc_conf_file):
|
|
try:
|
|
fsc_file = open(fsc_conf_file, 'r')
|
|
full_txt = fsc_file.readlines()
|
|
for line in full_txt:
|
|
if re.search('^manage_ip=', line):
|
|
manage_ip = line[len('manage_ip='):]
|
|
manage_ip = manage_ip.strip('\n')
|
|
return manage_ip
|
|
except Exception as e:
|
|
LOG.debug("Get manage ip failed, error=%s.", e)
|
|
finally:
|
|
fsc_file.close()
|
|
else:
|
|
LOG.debug("Fsc conf file not exist, file_name=%s.", fsc_conf_file)
|
|
|
|
def get_dsw_manage_ip(self):
|
|
return manage_ip
|
|
|
|
def start_execute_cmd(self, cmd, full_result_flag):
|
|
fsc_ip = self.get_ip_port()
|
|
manage_ip = self.get_manage_ip()
|
|
ip_num = len(fsc_ip)
|
|
|
|
LOG.debug("fsc_ip is %s", fsc_ip)
|
|
|
|
if ip_num <= 0:
|
|
return None
|
|
|
|
if ip_num > 3:
|
|
ip_num = 3
|
|
|
|
exec_result = ''
|
|
result = ''
|
|
if full_result_flag:
|
|
for ip in fsc_ip:
|
|
cmd_args = [CMD_BIN, '--manage_ip', manage_ip.replace(
|
|
'\n', ''), '--ip', ip.replace('\n', '')] + cmd.split()
|
|
LOG.debug("Dsware cmd_args is %s.", cmd_args)
|
|
|
|
exec_result, err = utils.execute(*cmd_args, run_as_root=True)
|
|
exec_result = exec_result.split('\n')
|
|
LOG.debug("Result is %s.", exec_result)
|
|
if exec_result:
|
|
for line in exec_result:
|
|
if re.search('^result=0', line):
|
|
return exec_result
|
|
elif re.search('^result=50150007', line):
|
|
return 'result=0'
|
|
elif re.search('^result=50150008', line):
|
|
return 'result=0'
|
|
elif re.search('^result=50', line):
|
|
return exec_result
|
|
return exec_result
|
|
else:
|
|
for ip in fsc_ip:
|
|
cmd_args = [CMD_BIN, '--manage_ip', manage_ip.replace(
|
|
'\n', ''), '--ip', ip.replace('\n', '')] + cmd.split()
|
|
LOG.debug("Dsware cmd_args is %s.", cmd_args)
|
|
|
|
exec_result, err = utils.execute(*cmd_args, run_as_root=True)
|
|
LOG.debug("Result is %s.", exec_result)
|
|
exec_result = exec_result.split('\n')
|
|
if exec_result:
|
|
for line in exec_result:
|
|
if re.search('^result=', line):
|
|
result = line
|
|
if re.search('^result=0', line):
|
|
return line
|
|
elif re.search('^result=50150007', line):
|
|
return 'result=0'
|
|
elif re.search('^result=50150008', line):
|
|
return 'result=0'
|
|
elif re.search('^result=50', line):
|
|
return line
|
|
return result
|
|
|
|
def create_volume(self, vol_name, pool_id, vol_size, thin_flag):
|
|
cmd = '--op createVolume' + ' ' + '--volName' + ' ' + six.text_type(
|
|
vol_name) + ' ' + '--poolId' + ' ' + six.text_type(
|
|
pool_id) + ' ' + '--volSize' + ' ' + six.text_type(
|
|
vol_size) + ' ' + '--thinFlag' + ' ' + six.text_type(thin_flag)
|
|
|
|
exec_result = self.start_execute_cmd(cmd, 0)
|
|
if exec_result:
|
|
if re.search('^result=0', exec_result):
|
|
return 0
|
|
else:
|
|
return exec_result[self.res_idx:]
|
|
else:
|
|
return 1
|
|
|
|
def extend_volume(self, vol_name, new_vol_size):
|
|
cmd = ''
|
|
cmd = '--op expandVolume' + ' ' + '--volName' + ' ' + six.text_type(
|
|
vol_name) + ' ' + '--volSize' + ' ' + six.text_type(new_vol_size)
|
|
|
|
exec_result = self.start_execute_cmd(cmd, 0)
|
|
if exec_result:
|
|
if re.search('^result=0', exec_result):
|
|
return 0
|
|
else:
|
|
return exec_result[self.res_idx:]
|
|
else:
|
|
return 1
|
|
|
|
def create_volume_from_snap(self, vol_name, vol_size, snap_name):
|
|
cmd = ('--op createVolumeFromSnap' + ' ') + (
|
|
'--volName' + ' ') + six.text_type(
|
|
vol_name) + ' ' + '--snapNameSrc' + ' ' + six.text_type(
|
|
snap_name) + ' ' + '--volSize' + ' ' + six.text_type(vol_size)
|
|
|
|
exec_result = self.start_execute_cmd(cmd, 0)
|
|
if exec_result:
|
|
if re.search('^result=0', exec_result):
|
|
return 0
|
|
else:
|
|
return exec_result[self.res_idx:]
|
|
else:
|
|
return 1
|
|
|
|
def create_fullvol_from_snap(self, vol_name, snap_name):
|
|
cmd = ('--op createFullVolumeFromSnap' + ' ') + (
|
|
'--volName' + ' ') + six.text_type(
|
|
vol_name) + ' ' + '--snapName' + ' ' + six.text_type(snap_name)
|
|
|
|
exec_result = self.start_execute_cmd(cmd, 0)
|
|
if exec_result:
|
|
if re.search('^result=0', exec_result):
|
|
return 0
|
|
else:
|
|
return exec_result[self.res_idx:]
|
|
else:
|
|
return 1
|
|
|
|
def create_volume_from_volume(self, vol_name, vol_size, src_vol_name):
|
|
retcode = 1
|
|
tmp_snap_name = six.text_type(vol_name) + '_tmp_snap'
|
|
|
|
retcode = self.create_snapshot(tmp_snap_name, src_vol_name, 0)
|
|
if 0 != retcode:
|
|
return retcode
|
|
|
|
retcode = self.create_volume(vol_name, 0, vol_size, 0)
|
|
if 0 != retcode:
|
|
self.delete_snapshot(tmp_snap_name)
|
|
return retcode
|
|
|
|
retcode = self.create_fullvol_from_snap(vol_name, tmp_snap_name)
|
|
if 0 != retcode:
|
|
self.delete_snapshot(tmp_snap_name)
|
|
self.delete_volume(vol_name)
|
|
return retcode
|
|
|
|
return 0
|
|
|
|
def create_clone_volume_from_volume(self, vol_name,
|
|
vol_size, src_vol_name):
|
|
retcode = 1
|
|
tmp_snap_name = six.text_type(src_vol_name) + '_DT_clnoe_snap'
|
|
|
|
retcode = self.create_snapshot(tmp_snap_name, src_vol_name, 0)
|
|
if 0 != retcode:
|
|
return retcode
|
|
|
|
retcode = self.create_volume_from_snap(
|
|
vol_name, vol_size, tmp_snap_name)
|
|
if 0 != retcode:
|
|
return retcode
|
|
|
|
return 0
|
|
|
|
def volume_info_analyze(self, vol_info):
|
|
local_volume_info = volume_info
|
|
|
|
if not vol_info:
|
|
local_volume_info['result'] = 1
|
|
return local_volume_info
|
|
|
|
local_volume_info['result'] = 0
|
|
|
|
vol_info_list = []
|
|
vol_info_list = re.split(',', vol_info)
|
|
for line in vol_info_list:
|
|
line = line.replace('\n', '')
|
|
if re.search('^vol_name=', line):
|
|
local_volume_info['vol_name'] = line[len('vol_name='):]
|
|
elif re.search('^father_name=', line):
|
|
local_volume_info['father_name'] = line[len('father_name='):]
|
|
elif re.search('^status=', line):
|
|
local_volume_info['status'] = line[len('status='):]
|
|
elif re.search('^vol_size=', line):
|
|
local_volume_info['vol_size'] = line[len('vol_size='):]
|
|
elif re.search('^real_size=', line):
|
|
local_volume_info['real_size'] = line[len('real_size='):]
|
|
elif re.search('^pool_id=', line):
|
|
local_volume_info['pool_id'] = line[len('pool_id='):]
|
|
elif re.search('^create_time=', line):
|
|
local_volume_info['create_time'] = line[len('create_time='):]
|
|
else:
|
|
LOG.error(_LE("Analyze key not exist, key=%s."),
|
|
six.text_type(line))
|
|
return local_volume_info
|
|
|
|
def query_volume(self, vol_name):
|
|
tmp_volume_info = volume_info
|
|
cmd = '--op queryVolume' + ' ' + '--volName' + ' ' + vol_name
|
|
|
|
exec_result = self.start_execute_cmd(cmd, 1)
|
|
if exec_result:
|
|
for line in exec_result:
|
|
if re.search('^result=', line):
|
|
if not re.search('^result=0', line):
|
|
tmp_volume_info['result'] = line[self.res_idx:]
|
|
return tmp_volume_info
|
|
for line in exec_result:
|
|
if re.search('^vol_name=' + vol_name, line):
|
|
tmp_volume_info = self.volume_info_analyze(line)
|
|
if six.text_type(0) == tmp_volume_info['status']:
|
|
tmp_snap_name = six.text_type(
|
|
vol_name) + '_tmp_snap'
|
|
self.delete_snapshot(tmp_snap_name)
|
|
return tmp_volume_info
|
|
|
|
tmp_volume_info['result'] = 1
|
|
return tmp_volume_info
|
|
|
|
def delete_volume(self, vol_name):
|
|
cmd = '--op deleteVolume' + ' ' + '--volName' + ' ' + vol_name
|
|
|
|
exec_result = self.start_execute_cmd(cmd, 0)
|
|
if exec_result:
|
|
if re.search('^result=0', exec_result):
|
|
return 0
|
|
else:
|
|
return exec_result[self.res_idx:]
|
|
else:
|
|
return 1
|
|
|
|
def create_snapshot(self, snap_name, vol_name, smart_flag):
|
|
cmd = '--op createSnapshot' + ' ' + '--volName' + ' ' + six.text_type(
|
|
vol_name) + ' ' + '--snapName' + ' ' + six.text_type(
|
|
snap_name) + ' ' + '--smartFlag' + ' ' + six.text_type(smart_flag)
|
|
|
|
exec_result = self.start_execute_cmd(cmd, 0)
|
|
if exec_result:
|
|
if re.search('^result=0', exec_result):
|
|
return 0
|
|
else:
|
|
return exec_result[self.res_idx:]
|
|
else:
|
|
return 1
|
|
|
|
def snap_info_analyze(self, info):
|
|
local_snap_info = snap_info.copy()
|
|
|
|
if not info:
|
|
local_snap_info['result'] = 1
|
|
return local_snap_info
|
|
|
|
local_snap_info['result'] = 0
|
|
|
|
snap_info_list = []
|
|
snap_info_list = re.split(',', info)
|
|
for line in snap_info_list:
|
|
line = line.replace('\n', '')
|
|
if re.search('^snap_name=', line):
|
|
local_snap_info['snap_name'] = line[len('snap_name='):]
|
|
elif re.search('^father_name=', line):
|
|
local_snap_info['father_name'] = line[len('father_name='):]
|
|
elif re.search('^status=', line):
|
|
local_snap_info['status'] = line[len('status='):]
|
|
elif re.search('^snap_size=', line):
|
|
local_snap_info['snap_size'] = line[len('snap_size='):]
|
|
elif re.search('^real_size=', line):
|
|
local_snap_info['real_size'] = line[len('real_size='):]
|
|
elif re.search('^pool_id=', line):
|
|
local_snap_info['pool_id'] = line[len('pool_id='):]
|
|
elif re.search('^delete_priority=', line):
|
|
local_snap_info['delete_priority'] = line[
|
|
len('delete_priority='):]
|
|
elif re.search('^create_time=', line):
|
|
local_snap_info['create_time'] = line[len('create_time='):]
|
|
else:
|
|
LOG.error(_LE("Analyze key not exist, key=%s."),
|
|
line)
|
|
|
|
return local_snap_info
|
|
|
|
def query_snap(self, snap_name):
|
|
tmp_snap_info = snap_info.copy()
|
|
cmd = '--op querySnapshot' + ' ' + '--snapName' + ' ' + snap_name
|
|
|
|
exec_result = self.start_execute_cmd(cmd, 1)
|
|
if exec_result:
|
|
for line in exec_result:
|
|
if re.search('^result=', line):
|
|
if not re.search('^result=0', line):
|
|
tmp_snap_info['result'] = line[self.res_idx:]
|
|
return tmp_snap_info
|
|
for line in exec_result:
|
|
if re.search('^snap_name=' + snap_name, line):
|
|
tmp_snap_info = self.snap_info_analyze(line)
|
|
return tmp_snap_info
|
|
|
|
tmp_snap_info['result'] = 1
|
|
return tmp_snap_info
|
|
|
|
def delete_snapshot(self, snap_name):
|
|
cmd = '--op deleteSnapshot' + ' ' + '--snapName' + ' ' + snap_name
|
|
|
|
exec_result = self.start_execute_cmd(cmd, 0)
|
|
if exec_result:
|
|
if re.search('^result=0', exec_result):
|
|
return 0
|
|
else:
|
|
return exec_result[self.res_idx:]
|
|
else:
|
|
return 1
|
|
|
|
def pool_info_analyze(self, info):
|
|
local_pool_info = pool_info.copy()
|
|
|
|
if not info:
|
|
local_pool_info['result'] = 1
|
|
return local_pool_info
|
|
|
|
local_pool_info['result'] = 0
|
|
|
|
pool_info_list = []
|
|
pool_info_list = re.split(',', info)
|
|
for line in pool_info_list:
|
|
line = line.replace('\n', '')
|
|
if re.search('^pool_id=', line):
|
|
local_pool_info['pool_id'] = line[len('pool_id='):]
|
|
elif re.search('^total_capacity=', line):
|
|
local_pool_info['total_capacity'] = line[
|
|
len('total_capacity='):]
|
|
elif re.search('^used_capacity=', line):
|
|
local_pool_info['used_capacity'] = line[len('used_capacity='):]
|
|
elif re.search('^alloc_capacity=', line):
|
|
local_pool_info['alloc_capacity'] = line[
|
|
len('alloc_capacity='):]
|
|
else:
|
|
LOG.error(_LE("Analyze key not exist, key=%s."),
|
|
six.text_type(line))
|
|
return local_pool_info
|
|
|
|
def query_pool_info(self, pool_id):
|
|
tmp_pool_info = pool_info.copy()
|
|
cmd = '--op queryPoolInfo' + ' ' + '--poolId' + ' ' + six.text_type(
|
|
pool_id)
|
|
LOG.debug("Pool id is %s.", pool_id)
|
|
exec_result = self.start_execute_cmd(cmd, 1)
|
|
if exec_result:
|
|
for line in exec_result:
|
|
if re.search('^result=', line):
|
|
if not re.search('^result=0', line):
|
|
tmp_pool_info['result'] = line[self.res_idx:]
|
|
return tmp_pool_info
|
|
for line in exec_result:
|
|
if re.search('^pool_id=' + six.text_type(pool_id),
|
|
line):
|
|
tmp_pool_info = self.pool_info_analyze(line)
|
|
return tmp_pool_info
|
|
|
|
tmp_pool_info['result'] = 1
|
|
return tmp_pool_info
|
|
|
|
def query_pool_type(self, pool_type):
|
|
pool_list = []
|
|
tmp_pool_info = {}
|
|
result = 0
|
|
cmd = ''
|
|
cmd = '--op queryPoolType --poolType' + ' ' + pool_type
|
|
LOG.debug("Query poolType: %s.", pool_type)
|
|
exec_result = self.start_execute_cmd(cmd, 1)
|
|
if exec_result:
|
|
for line in exec_result:
|
|
line = line.replace('\n', '')
|
|
if re.search('^result=', line):
|
|
if not re.search('^result=0', line):
|
|
result = int(line[self.res_idx:])
|
|
break
|
|
for one_line in exec_result:
|
|
if re.search('^pool_id=', one_line):
|
|
tmp_pool_info = self.pool_info_analyze(one_line)
|
|
pool_list.append(tmp_pool_info)
|
|
break
|
|
return (result, pool_list)
|
|
|
|
def query_dsware_version(self):
|
|
retcode = 2
|
|
cmd = '--op getDSwareIdentifier'
|
|
exec_result = self.start_execute_cmd(cmd, 0)
|
|
if exec_result:
|
|
# New version.
|
|
if re.search('^result=0', exec_result):
|
|
retcode = 0
|
|
# Old version.
|
|
elif re.search('^result=50500001', exec_result):
|
|
retcode = 1
|
|
# Failed!
|
|
else:
|
|
retcode = exec_result[self.res_idx:]
|
|
return retcode
|