Beispiel #1
0
    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
Beispiel #2
0
    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)
Beispiel #3
0
    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
Beispiel #4
0
    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)
Beispiel #5
0
    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)
Beispiel #6
0
 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)
Beispiel #7
0
 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)
Beispiel #8
0
    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)
Beispiel #9
0
    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
        ]
Beispiel #10
0
    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)
Beispiel #11
0
    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']
Beispiel #12
0
    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']
Beispiel #13
0
    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)
Beispiel #14
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]
Beispiel #15
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]