def _vcr_cap_get(self, mega_sys_path): cap_output = self._storcli_exec([mega_sys_path, "show", "all"])['Capabilities'] mega_raid_types = \ cap_output['RAID Level Supported'].replace(', \n', '').split(', ') supported_raid_types = [] for cur_mega_raid_type in list(_RAID_TYPE_MAP.keys()): if cur_mega_raid_type in mega_raid_types: supported_raid_types.append(_RAID_TYPE_MAP[cur_mega_raid_type]) supported_raid_types = sorted(list(set(supported_raid_types))) min_strip_size = _mega_size_to_lsm(cap_output['Min Strip Size']) max_strip_size = _mega_size_to_lsm(cap_output['Max Strip Size']) supported_strip_sizes = list(min_strip_size * (2**i) for i in range( 0, int(math.log(int_div(max_strip_size, min_strip_size), 2) + 1))) # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # The math above is to generate a list like: # min_strip_size, ... n^2 , max_strip_size return supported_raid_types, supported_strip_sizes
def volumes(self, search_key=None, search_value=None, flags=0): """ Returns an array of volume objects """ vol_list = [] lu_list = self._request("get_names", "zvol", [""]) # lu_list = self._ns_request('rest/nms', # {"method": "get_lu_list", # "object": "scsidisk", # "params": ['']}) for lu in lu_list: try: lu_props = self._request("get_lu_props", "scsidisk", [lu]) except: lu_props = {'guid': '', 'state': 'N/A'} zvol_props = self._request("get_child_props", "zvol", [lu, ""]) block_size = NexentaStor._to_bytes(zvol_props['volblocksize']) size_bytes = int(zvol_props['size_bytes']) num_of_blocks = int_div(size_bytes, block_size) admin_state = Volume.ADMIN_STATE_ENABLED vol_list.append( Volume(lu, lu, lu_props['guid'].lower(), block_size, num_of_blocks, admin_state, self.system.id, NexentaStor._get_pool_id(lu))) return search_property(vol_list, search_key, search_value)
def _vcr_cap_get(self, mega_sys_path): cap_output = self._storcli_exec( [mega_sys_path, "show", "all"])['Capabilities'] mega_raid_types = \ cap_output['RAID Level Supported'].replace(', \n', '').split(', ') supported_raid_types = [] for cur_mega_raid_type in list(_RAID_TYPE_MAP.keys()): if cur_mega_raid_type in mega_raid_types: supported_raid_types.append( _RAID_TYPE_MAP[cur_mega_raid_type]) supported_raid_types = sorted(list(set(supported_raid_types))) min_strip_size = _mega_size_to_lsm(cap_output['Min Strip Size']) max_strip_size = _mega_size_to_lsm(cap_output['Max Strip Size']) supported_strip_sizes = list( min_strip_size * (2 ** i) for i in range( 0, int(math.log(int_div(max_strip_size, min_strip_size), 2) + 1))) # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # The math above is to generate a list like: # min_strip_size, ... n^2 , max_strip_size return supported_raid_types, supported_strip_sizes
def _get_volume(self, pool_id, volume_name): vol = [ v for v in self._jsonrequest("vol_list", dict(pool=pool_id)) if v['name'] == volume_name ][0] vpd83 = TargetdStorage._uuid_to_vpd83(vol['uuid']) return Volume(vol['uuid'], vol['name'], vpd83, 512, int_div(vol['size'], 512), Volume.ADMIN_STATE_ENABLED, self.system.id, pool_id)
def _get_volume(self, pool_id, volume_name): vol = [v for v in self._jsonrequest("vol_list", dict(pool=pool_id)) if v['name'] == volume_name][0] vpd83 = TargetdStorage._uuid_to_vpd83(vol['uuid']) return Volume(vol['uuid'], vol['name'], vpd83, 512, int_div(vol['size'], 512), Volume.ADMIN_STATE_ENABLED, self.system.id, pool_id)
def volumes(self, search_key=None, search_value=None, flags=0): volumes = [] for p_name in (p['name'] for p in self._jsonrequest("pool_list") if p['type'] == 'block'): for vol in self._jsonrequest("vol_list", dict(pool=p_name)): vpd83 = TargetdStorage._uuid_to_vpd83(vol['uuid']) volumes.append( Volume(vol['uuid'], vol['name'], vpd83, 512, long(int_div(vol['size'], 512)), Volume.ADMIN_STATE_ENABLED, self.system.id, p_name)) return search_property(volumes, search_key, search_value)
def volume_resize(self, volume, new_size_bytes, flags=0): """ Re-sizes a volume. Returns a tuple (job_id, re-sized_volume) Note: Tuple return values are mutually exclusive, when one is None the other must be valid. """ self._request("set_child_prop", "zvol", [volume.name, 'volsize', str(new_size_bytes)]) self._request("realign_size", "scsidisk", [volume.name]) new_num_of_blocks = int_div(new_size_bytes, volume.block_size) return None, Volume(volume.id, volume.name, volume.vpd83, volume.block_size, new_num_of_blocks, volume.admin_state, volume.system_id, volume.pool_id)
def volume_raid_info(self, volume, flags=Client.FLAG_RSVD): if not volume.plugin_data: raise LsmError( ErrorNumber.INVALID_ARGUMENT, "Ilegal input volume argument: missing plugin_data property") vd_path = _vd_path_of_lsm_vol(volume) vol_show_output = self._storcli_exec([vd_path, "show", "all"]) vd_basic_info = vol_show_output[vd_path][0] vd_id = int(vd_basic_info['DG/VD'].split('/')[-1]) vd_prop_info = vol_show_output['VD%d Properties' % vd_id] raid_type = _mega_raid_type_to_lsm(vd_basic_info, vd_prop_info) strip_size = _mega_size_to_lsm(vd_prop_info['Strip Size']) disk_count = (int(vd_prop_info['Number of Drives Per Span']) * int(vd_prop_info['Span Depth'])) if raid_type == Volume.RAID_TYPE_RAID0: strip_count = disk_count elif raid_type == Volume.RAID_TYPE_RAID1: strip_count = 1 elif raid_type == Volume.RAID_TYPE_RAID5: strip_count = disk_count - 1 elif raid_type == Volume.RAID_TYPE_RAID6: strip_count = disk_count - 2 elif raid_type == Volume.RAID_TYPE_RAID50: strip_count = ( (int(vd_prop_info['Number of Drives Per Span']) - 1) * int(vd_prop_info['Span Depth'])) elif raid_type == Volume.RAID_TYPE_RAID60: strip_count = ( (int(vd_prop_info['Number of Drives Per Span']) - 2) * int(vd_prop_info['Span Depth'])) elif raid_type == Volume.RAID_TYPE_RAID10: strip_count = ( int_div(int(vd_prop_info['Number of Drives Per Span']), 2) * int(vd_prop_info['Span Depth'])) else: # MegaRAID does not support 15 or 16 yet. raise LsmError( ErrorNumber.PLUGIN_BUG, "volume_raid_info(): Got unexpected RAID type: %s" % vd_basic_info['TYPE']) return [ raid_type, strip_size, disk_count, strip_size, strip_size * strip_count ]
def _ns_request(self, path, data): response = None parms = json.dumps(data) url = '%s://%s:%s/%s' % \ (self._scheme, self.uparse.hostname, self._port, path) request = Request(url, parms.encode('utf-8')) username = self.uparse.username or 'admin' user_name_pass = '******' % (username, self.password) auth = base64.b64encode(user_name_pass.encode('utf-8')).decode('utf-8') request.add_header('Authorization', 'Basic %s' % auth) request.add_header('Content-Type', 'application/json') try: response = urlopen(request, timeout=int_div(self.timeout, 1000)) except Exception as e: try: common_urllib2_error_handler(e) except LsmError as lsm_e: exc_info = sys.exc_info() if lsm_e.code == ErrorNumber.NETWORK_CONNREFUSED: if not self.uparse.port and \ self._port == NexentaStor._V3_PORT: self._port = NexentaStor._V4_PORT return self._ns_request(path, data) six.reraise(*exc_info) resp_json = response.read().decode('utf-8') resp = json.loads(resp_json) if resp['error']: if 'message' in resp['error']: msg = resp['error']['message'] # Check to see if there is a better way to do this... if 'dataset already exists' in msg: raise LsmError(ErrorNumber.NAME_CONFLICT, msg) if 'Unable to destroy hostgroup' in msg: raise LsmError(ErrorNumber.IS_MASKED, msg) raise LsmError(ErrorNumber.PLUGIN_BUG, resp['error']) return resp['result']
def volumes(self, search_key=None, search_value=None, flags=0): """ Returns an array of volume objects """ vol_list = [] lu_list = self._request("get_names", "zvol", [""]) # lu_list = self._ns_request('rest/nms', # {"method": "get_lu_list", # "object": "scsidisk", # "params": ['']}) for lu in lu_list: try: try: lu_props = self._request("get_lu_props", "scsidisk", [lu]) except: lu_props = {'guid': '', 'state': 'N/A'} zvol_props = self._request("get_child_props", "zvol", [lu, ""]) block_size = NexentaStor._to_bytes(zvol_props['volblocksize']) size_bytes = int(zvol_props['size_bytes']) num_of_blocks = int_div(size_bytes, block_size) admin_state = Volume.ADMIN_STATE_ENABLED vol_list.append( Volume(lu, lu, lu_props['guid'].lower(), block_size, num_of_blocks, admin_state, self.system.id, NexentaStor._get_pool_id(lu))) except LsmError as e: # The available volumes could have changed while we were trying # to retrieve information about each one of them. error('nstor:volumes: %s' % str(e)) pass return search_property(vol_list, search_key, search_value)
def volume_raid_create(self, name, raid_type, disks, strip_size, flags=Client.FLAG_RSVD): """ Work flow: 1. Create RAID volume storcli /c0 add vd RAID10 drives=252:1-4 pdperarray=2 J 2. Find out pool/DG base on one disk. storcli /c0/e252/s1 show J 3. Find out the volume/VD base on pool/DG using self.volumes() """ mega_raid_type = _lsm_raid_type_to_mega(raid_type) ctrl_num = None slot_nums = [] enclosure_str = None for disk in disks: if not disk.plugin_data: raise LsmError( ErrorNumber.INVALID_ARGUMENT, "Illegal input disks argument: missing plugin_data " "property") # Disk should from the same controller. # Please be informed, the enclosure_str could be a empty(space). (cur_ctrl_num, cur_enclosure_str, slot_num) = \ disk.plugin_data.split(':') cur_ctrl_num = int(cur_ctrl_num) if ctrl_num is not None and cur_ctrl_num != ctrl_num: raise LsmError( ErrorNumber.INVALID_ARGUMENT, "Illegal input disks argument: disks are not from the " "same controller/system.") if enclosure_str is not None and \ cur_enclosure_str != enclosure_str: raise LsmError( ErrorNumber.INVALID_ARGUMENT, "Illegal input disks argument: disks are not from the " "same disk enclosure.") ctrl_num = cur_ctrl_num enclosure_str = cur_enclosure_str slot_nums.append(slot_num) # Handle request volume name, LSI only allow 15 characters. name = re.sub('[^0-9a-zA-Z_\-]+', '', name)[:15] if enclosure_str == ' ': drives_str = "drives=%s" % ','.join(slot_nums) else: drives_str = "drives=%s:%s" % (enclosure_str, ','.join(slot_nums)) cmds = [ "/c%s" % ctrl_num, "add", "vd", mega_raid_type, 'size=all', "name=%s" % name, drives_str ] if raid_type == Volume.RAID_TYPE_RAID10 or \ raid_type == Volume.RAID_TYPE_RAID50 or \ raid_type == Volume.RAID_TYPE_RAID60: cmds.append("pdperarray=%d" % int(int_div(len(disks), 2))) if strip_size != Volume.VCR_STRIP_SIZE_DEFAULT: cmds.append("strip=%d" % int(int_div(strip_size, 1024))) try: self._storcli_exec(cmds) except ExecError: req_disk_ids = [d.id for d in disks] for cur_disk in self.disks(): if cur_disk.id in req_disk_ids and \ not cur_disk.status & Disk.STATUS_FREE: raise LsmError( ErrorNumber.DISK_NOT_FREE, "Disk %s is not in STATUS_FREE state" % cur_disk.id) # Check whether got unsupported RAID type or stripe size supported_raid_types, supported_strip_sizes = \ self._vcr_cap_get("/c%s" % ctrl_num) if raid_type not in supported_raid_types: raise LsmError(ErrorNumber.NO_SUPPORT, "Provided 'raid_type' is not supported") if strip_size != Volume.VCR_STRIP_SIZE_DEFAULT and \ strip_size not in supported_strip_sizes: raise LsmError(ErrorNumber.NO_SUPPORT, "Provided 'strip_size' is not supported") raise # Find out the DG ID from one disk. dg_show_output = self._storcli_exec( ["/c%s/e%s/s%s" % tuple(disks[0].plugin_data.split(":")), "show"]) dg_id = dg_show_output['Drive Information'][0]['DG'] if dg_id == '-': raise LsmError( ErrorNumber.PLUGIN_BUG, "volume_raid_create(): No error found in output, " "but RAID is not created: %s" % list(dg_show_output.items())) else: dg_id = int(dg_id) pool_id = _pool_id_of(dg_id, self._sys_id_of_ctrl_num(ctrl_num)) lsm_vols = self.volumes(search_key='pool_id', search_value=pool_id) if len(lsm_vols) != 1: raise LsmError( ErrorNumber.PLUGIN_BUG, "volume_raid_create(): Got unexpected volume count(not 1) " "when creating RAID volume") return lsm_vols[0]
def volume_raid_create(self, name, raid_type, disks, strip_size, flags=Client.FLAG_RSVD): """ Work flow: 1. Create RAID volume storcli /c0 add vd RAID10 drives=252:1-4 pdperarray=2 J 2. Find out pool/DG base on one disk. storcli /c0/e252/s1 show J 3. Find out the volume/VD base on pool/DG using self.volumes() """ mega_raid_type = _lsm_raid_type_to_mega(raid_type) ctrl_num = None slot_nums = [] enclosure_str = None for disk in disks: if not disk.plugin_data: raise LsmError( ErrorNumber.INVALID_ARGUMENT, "Illegal input disks argument: missing plugin_data " "property") # Disk should from the same controller. # Please be informed, the enclosure_str could be a empty(space). (cur_ctrl_num, cur_enclosure_str, slot_num) = \ disk.plugin_data.split(':') cur_ctrl_num = int(cur_ctrl_num) if ctrl_num is not None and cur_ctrl_num != ctrl_num: raise LsmError( ErrorNumber.INVALID_ARGUMENT, "Illegal input disks argument: disks are not from the " "same controller/system.") if enclosure_str is not None and \ cur_enclosure_str != enclosure_str: raise LsmError( ErrorNumber.INVALID_ARGUMENT, "Illegal input disks argument: disks are not from the " "same disk enclosure.") ctrl_num = cur_ctrl_num enclosure_str = cur_enclosure_str slot_nums.append(slot_num) # Handle request volume name, LSI only allow 15 characters. name = re.sub('[^0-9a-zA-Z_\-]+', '', name)[:15] if enclosure_str == ' ': drives_str = "drives=%s" % ','.join(slot_nums) else: drives_str = "drives=%s:%s" % (enclosure_str, ','.join(slot_nums)) cmds = [ "/c%s" % ctrl_num, "add", "vd", mega_raid_type, 'size=all', "name=%s" % name, drives_str] if raid_type == Volume.RAID_TYPE_RAID10 or \ raid_type == Volume.RAID_TYPE_RAID50 or \ raid_type == Volume.RAID_TYPE_RAID60: cmds.append("pdperarray=%d" % int(int_div(len(disks), 2))) if strip_size != Volume.VCR_STRIP_SIZE_DEFAULT: cmds.append("strip=%d" % int(int_div(strip_size, 1024))) try: self._storcli_exec(cmds) except ExecError: req_disk_ids = [d.id for d in disks] for cur_disk in self.disks(): if cur_disk.id in req_disk_ids and \ not cur_disk.status & Disk.STATUS_FREE: raise LsmError( ErrorNumber.DISK_NOT_FREE, "Disk %s is not in STATUS_FREE state" % cur_disk.id) # Check whether got unsupported RAID type or stripe size supported_raid_types, supported_strip_sizes = \ self._vcr_cap_get("/c%s" % ctrl_num) if raid_type not in supported_raid_types: raise LsmError( ErrorNumber.NO_SUPPORT, "Provided 'raid_type' is not supported") if strip_size != Volume.VCR_STRIP_SIZE_DEFAULT and \ strip_size not in supported_strip_sizes: raise LsmError( ErrorNumber.NO_SUPPORT, "Provided 'strip_size' is not supported") raise # Find out the DG ID from one disk. dg_show_output = self._storcli_exec( ["/c%s/e%s/s%s" % tuple(disks[0].plugin_data.split(":")), "show"]) dg_id = dg_show_output['Drive Information'][0]['DG'] if dg_id == '-': raise LsmError( ErrorNumber.PLUGIN_BUG, "volume_raid_create(): No error found in output, " "but RAID is not created: %s" % list(dg_show_output.items())) else: dg_id = int(dg_id) pool_id = _pool_id_of(dg_id, self._sys_id_of_ctrl_num(ctrl_num)) lsm_vols = self.volumes(search_key='pool_id', search_value=pool_id) if len(lsm_vols) != 1: raise LsmError( ErrorNumber.PLUGIN_BUG, "volume_raid_create(): Got unexpected volume count(not 1) " "when creating RAID volume") return lsm_vols[0]