def connect_volume(self, connection_properties):
        """Connect to a volume.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes;
                                      it needs to contain the StorPool
                                      'client_id' and the common 'volume' and
                                      'access_mode' values.
        :type connection_properties: dict
        :returns: dict
        """
        client_id = connection_properties.get('client_id', None)
        if client_id is None:
            raise exception.BrickException(_LE(
                'Invalid StorPool connection data, no client ID specified.'))
        volume_id = connection_properties.get('volume', None)
        if volume_id is None:
            raise exception.BrickException(_LE(
                'Invalid StorPool connection data, no volume ID specified.'))
        volume = self._attach.volumeName(volume_id)
        mode = connection_properties.get('access_mode', None)
        if mode is None or mode not in ('rw', 'ro'):
            raise exception.BrickException(_LE(
                'Invalid access_mode specified in the connection data.'))
        req_id = 'brick-%s-%s' % (client_id, volume_id)
        self._attach.add(req_id, {
            'volume': volume,
            'type': 'brick',
            'id': req_id,
            'rights': 1 if mode == 'ro' else 2,
            'volsnap': False
        })
        self._attach.sync(req_id, None)
        return {'type': 'block', 'path': '/dev/storpool/' + volume}
    def get_volume_paths(self, connection_properties):
        """Return the list of existing paths for a volume.

        The job of this method is to find out what paths in
        the system are associated with a volume as described
        by the connection_properties.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes;
                                      it needs to contain 'volume' and
                                      'device_path' values.
        :type connection_properties: dict
        """
        volume_id = connection_properties.get('volume', None)
        if volume_id is None:
            raise exception.BrickException(_LE(
                'Invalid StorPool connection data, no volume ID specified.'))
        volume = self._attach.volumeName(volume_id)
        path = '/dev/storpool/' + volume
        dpath = connection_properties.get('device_path', None)
        if dpath is not None and dpath != path:
            raise exception.BrickException(_LE(
                'Internal error: StorPool volume path {path} does not '
                'match device path {dpath}'),
                {path: path, dpath: dpath})
        return [path]
Пример #3
0
    def __init__(self, message=None, **kwargs):
        self.kwargs = kwargs
        self.kwargs['message'] = message

        if 'code' not in self.kwargs:
            try:
                self.kwargs['code'] = self.code
            except AttributeError:
                pass

        for k, v in self.kwargs.items():
            if isinstance(v, Exception):
                self.kwargs[k] = six.text_type(v)

        if self._should_format():
            try:
                message = self.message % kwargs

            except Exception:
                # kwargs doesn't match a variable in the message
                # log the issue and the kwargs
                LOG.exception(_LE("Exception in string format operation. "
                                  "msg='%s'"), self.message)
                for name, value in kwargs.items():
                    LOG.error(_LE("%(name)s: %(value)s"), {'name': name,
                                                           'value': value})

                # at least get the core message out if something happened
                message = self.message

        # Put the message in 'msg' so that we can access it.  If we have it in
        # message it will be overshadowed by the class' message attribute
        self.msg = message
        super(BrickException, self).__init__(message)
    def disconnect_volume(self, connection_properties, device_info):
        """Disconnect a volume from the local host.

        The connection_properties are the same as from connect_volume.
        The device_info is returned from connect_volume.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes;
                                      it needs to contain the StorPool
                                      'client_id' and the common 'volume'
                                      values.
        :type connection_properties: dict
        :param device_info: historical difference, but same as connection_props
        :type device_info: dict
        """
        client_id = connection_properties.get('client_id', None)
        if client_id is None:
            raise exception.BrickException(_LE(
                'Invalid StorPool connection data, no client ID specified.'))
        volume_id = connection_properties.get('volume', None)
        if volume_id is None:
            raise exception.BrickException(_LE(
                'Invalid StorPool connection data, no volume ID specified.'))
        volume = self._attach.volumeName(volume_id)
        req_id = 'brick-%s-%s' % (client_id, volume_id)
        self._attach.sync(req_id, volume)
        self._attach.remove(req_id)
Пример #5
0
    def __init__(self, message=None, **kwargs):
        self.kwargs = kwargs

        if 'code' not in self.kwargs:
            try:
                self.kwargs['code'] = self.code
            except AttributeError:
                pass

        if not message:
            try:
                message = self.message % kwargs

            except Exception:
                # kwargs doesn't match a variable in the message
                # log the issue and the kwargs
                LOG.exception(
                    _LE("Exception in string format operation. "
                        "msg='%s'"), self.message)
                for name, value in kwargs.items():
                    LOG.error(_LE("%(name)s: %(value)s"), {
                        'name': name,
                        'value': value
                    })

                # at least get the core message out if something happened
                message = self.message

        # Put the message in 'msg' so that we can access it.  If we have it in
        # message it will be overshadowed by the class' message attribute
        self.msg = message
        super(BrickException, self).__init__(message)
Пример #6
0
    def disconnect_volume(self, connection_properties, device_info):
        """Disconnect a volume from the local host.

        The connection_properties are the same as from connect_volume.
        The device_info is returned from connect_volume.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes;
                                      it needs to contain the StorPool
                                      'client_id' and the common 'volume'
                                      values.
        :type connection_properties: dict
        :param device_info: historical difference, but same as connection_props
        :type device_info: dict
        """
        client_id = connection_properties.get('client_id', None)
        if client_id is None:
            raise exception.BrickException(
                _LE('Invalid StorPool connection data, no client ID specified.'
                    ))
        volume_id = connection_properties.get('volume', None)
        if volume_id is None:
            raise exception.BrickException(
                _LE('Invalid StorPool connection data, no volume ID specified.'
                    ))
        volume = self._attach.volumeName(volume_id)
        req_id = 'brick-%s-%s' % (client_id, volume_id)
        self._attach.sync(req_id, volume)
        self._attach.remove(req_id)
Пример #7
0
    def create_lv_snapshot(self, name, source_lv_name, lv_type='default'):
        """Creates a snapshot of a logical volume.

        :param name: Name to assign to new snapshot
        :param source_lv_name: Name of Logical Volume to snapshot
        :param lv_type: Type of LV (default or thin)

        """
        source_lvref = self.get_volume(source_lv_name)
        if source_lvref is None:
            LOG.error(_LE("Trying to create snapshot by non-existent LV: %s"),
                      source_lv_name)
            raise exception.VolumeDeviceNotFound(device=source_lv_name)
        cmd = LVM.LVM_CMD_PREFIX + ['lvcreate', '--name', name, '--snapshot',
                                    '%s/%s' % (self.vg_name, source_lv_name)]
        if lv_type != 'thin':
            size = source_lvref['size']
            cmd.extend(['-L', '%sg' % (size)])

        try:
            self._execute(*cmd,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error creating snapshot'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise
Пример #8
0
    def create_lv_snapshot(self, name, source_lv_name, lv_type='default'):
        """Creates a snapshot of a logical volume.

        :param name: Name to assign to new snapshot
        :param source_lv_name: Name of Logical Volume to snapshot
        :param lv_type: Type of LV (default or thin)

        """
        source_lvref = self.get_volume(source_lv_name)
        if source_lvref is None:
            LOG.error(_LE("Trying to create snapshot by non-existent LV: %s"),
                      source_lv_name)
            raise exception.VolumeDeviceNotFound(device=source_lv_name)
        cmd = LVM.LVM_CMD_PREFIX + [
            'lvcreate', '--name', name, '--snapshot',
            '%s/%s' % (self.vg_name, source_lv_name)
        ]
        if lv_type != 'thin':
            size = source_lvref['size']
            cmd.extend(['-L', '%sg' % (size)])

        try:
            self._execute(*cmd,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error creating snapshot'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise
Пример #9
0
    def get_volume_paths(self, connection_properties):
        """Return the list of existing paths for a volume.

        The job of this method is to find out what paths in
        the system are associated with a volume as described
        by the connection_properties.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes;
                                      it needs to contain 'volume' and
                                      'device_path' values.
        :type connection_properties: dict
        """
        volume_id = connection_properties.get('volume', None)
        if volume_id is None:
            raise exception.BrickException(
                _LE('Invalid StorPool connection data, no volume ID specified.'
                    ))
        volume = self._attach.volumeName(volume_id)
        path = '/dev/storpool/' + volume
        dpath = connection_properties.get('device_path', None)
        if dpath is not None and dpath != path:
            raise exception.BrickException(
                _LE('Internal error: StorPool volume path {path} does not '
                    'match device path {dpath}'), {
                        path: path,
                        dpath: dpath
                    })
        return [path]
Пример #10
0
    def rename_volume(self, lv_name, new_name):
        """Change the name of an existing volume."""

        try:
            self._execute('lvrename', self.vg_name, lv_name, new_name,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error renaming logical volume'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise
Пример #11
0
 def deactivate_lv(self, name):
     lv_path = self.vg_name + '/' + self._mangle_lv_name(name)
     cmd = ['lvchange', '-a', 'n']
     cmd.append(lv_path)
     try:
         self._execute(*cmd,
                       root_helper=self._root_helper,
                       run_as_root=True)
     except putils.ProcessExecutionError as err:
         LOG.exception(_LE('Error deactivating LV'))
         LOG.error(_LE('Cmd     :%s'), err.cmd)
         LOG.error(_LE('StdOut  :%s'), err.stdout)
         LOG.error(_LE('StdErr  :%s'), err.stderr)
         raise
Пример #12
0
 def deactivate_lv(self, name):
     lv_path = self.vg_name + '/' + self._mangle_lv_name(name)
     cmd = ['lvchange', '-a', 'n']
     cmd.append(lv_path)
     try:
         self._execute(*cmd,
                       root_helper=self._root_helper,
                       run_as_root=True)
     except putils.ProcessExecutionError as err:
         LOG.exception(_LE('Error deactivating LV'))
         LOG.error(_LE('Cmd     :%s'), err.cmd)
         LOG.error(_LE('StdOut  :%s'), err.stdout)
         LOG.error(_LE('StdErr  :%s'), err.stderr)
         raise
Пример #13
0
    def _connect_tcp_socket(self, client_ip, client_port):
        """Connect to TCP socket."""
        sock = None

        for res in socket.getaddrinfo(client_ip,
                                      client_port,
                                      socket.AF_UNSPEC,
                                      socket.SOCK_STREAM):
                aff, socktype, proto, canonname, saa = res
                try:
                    sock = socket.socket(aff, socktype, proto)
                except socket.error:
                    sock = None
                    continue
                try:
                    sock.connect(saa)
                except socket.error:
                    sock.close()
                    sock = None
                    continue
                break

        if sock is None:
            LOG.error(_LE("Cannot connect TCP socket"))
        return sock
Пример #14
0
    def _get_hba_channel_scsi_target(self, hba):
        """Try to get the HBA channel and SCSI target for an HBA.

        This method only works for Fibre Channel targets that implement a
        single WWNN for all ports, so caller should expect us to return either
        None or an empty list.

        :returns: List or None
        """
        # Leave only the number from the host_device field (ie: host6)
        host_device = hba['host_device']
        if host_device and len(host_device) > 4:
            host_device = host_device[4:]

        path = '/sys/class/fc_transport/target%s:' % host_device
        cmd = 'grep %(wwnn)s %(path)s*/node_name' % {
            'wwnn': hba['node_name'],
            'path': path
        }
        try:
            out, _err = self._execute(cmd)
            return [
                line.split('/')[4].split(':')[1:] for line in out.split('\n')
                if line.startswith(path)
            ]
        except Exception as exc:
            LOG.error(
                _LE('Could not get HBA channel and SCSI target ID, '
                    'path: %(path)s, reason: %(reason)s'), {
                        'path': path,
                        'reason': exc
                    })
            return None
Пример #15
0
    def rename_volume(self, lv_name, new_name):
        """Change the name of an existing volume."""

        try:
            self._execute('lvrename',
                          self.vg_name,
                          lv_name,
                          new_name,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error renaming logical volume'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise
    def __init__(self, root_helper, driver=None,
                 *args, **kwargs):

        super(StorPoolConnector, self).__init__(root_helper, driver=driver,
                                                *args, **kwargs)

        if storpool is None:
            raise exception.BrickException(_LE(
                'Could not import the StorPool API bindings'))

        try:
            self._attach = spopenstack.AttachDB(log=LOG)
            self._attach.api()
        except Exception as e:
            raise exception.BrickException(_LE(
                'Could not initialize the StorPool API bindings: %s') % (e))
Пример #17
0
def get_volume_encryptor(root_helper,
                         connection_info,
                         keymgr,
                         execute=None,
                         *args, **kwargs):
    """Creates a VolumeEncryptor used to encrypt the specified volume.

    :param: the connection information used to attach the volume
    :returns VolumeEncryptor: the VolumeEncryptor for the volume
    """
    encryptor = nop.NoOpEncryptor(root_helper=root_helper,
                                  connection_info=connection_info,
                                  keymgr=keymgr,
                                  execute=execute,
                                  *args, **kwargs)

    location = kwargs.get('control_location', None)
    if location and location.lower() == 'front-end':  # case insensitive
        provider = kwargs.get('provider')

        # TODO(lyarwood): Remove the following in Pike and raise an
        # ERROR if provider is not a key in SUPPORTED_ENCRYPTION_PROVIDERS.
        # Until then continue to allow both the class name and path to be used.
        if provider in LEGACY_PROVIDER_CLASS_TO_FORMAT_MAP:
            LOG.warning(_LW("Use of the in tree encryptor class %(provider)s"
                            " by directly referencing the implementation class"
                            " will be blocked in the Pike release of"
                            " os-brick."), {'provider': provider})
            provider = LEGACY_PROVIDER_CLASS_TO_FORMAT_MAP[provider]

        if provider in FORMAT_TO_FRONTEND_ENCRYPTOR_MAP:
            provider = FORMAT_TO_FRONTEND_ENCRYPTOR_MAP[provider]
        elif provider is None:
            provider = "os_brick.encryptors.nop.NoOpEncryptor"
        else:
            LOG.warning(_LW("Use of the out of tree encryptor class "
                            "%(provider)s will be blocked with the Pike "
                            "release of os-brick."), {'provider': provider})

        try:
            encryptor = importutils.import_object(
                provider,
                root_helper,
                connection_info,
                keymgr,
                execute,
                **kwargs)
        except Exception as e:
            LOG.error(_LE("Error instantiating %(provider)s: %(exception)s"),
                      {'provider': provider, 'exception': e})
            raise

    msg = ("Using volume encryptor '%(encryptor)s' for connection: "
           "%(connection_info)s" %
           {'encryptor': encryptor, 'connection_info': connection_info})
    LOG.debug(strutils.mask_password(msg))

    return encryptor
Пример #18
0
    def deactivate_lv(self, name):
        lv_path = self.vg_name + '/' + self._mangle_lv_name(name)
        cmd = ['lvchange', '-a', 'n']
        cmd.append(lv_path)
        try:
            self._execute(*cmd,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error deactivating LV'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise

        # Wait until lv is deactivated to return in
        # order to prevent a race condition.
        self._wait_for_volume_deactivation(name)
Пример #19
0
    def __init__(self, root_helper, driver=None, *args, **kwargs):

        super(StorPoolConnector, self).__init__(root_helper,
                                                driver=driver,
                                                *args,
                                                **kwargs)

        if storpool is None:
            raise exception.BrickException(
                _LE('Could not import the StorPool API bindings'))

        try:
            self._attach = spopenstack.AttachDB(log=LOG)
            self._attach.api()
        except Exception as e:
            raise exception.BrickException(
                _LE('Could not initialize the StorPool API bindings: %s') %
                (e))
Пример #20
0
    def deactivate_lv(self, name):
        lv_path = self.vg_name + '/' + self._mangle_lv_name(name)
        cmd = ['lvchange', '-a', 'n']
        cmd.append(lv_path)
        try:
            self._execute(*cmd,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error deactivating LV'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise

        # Wait until lv is deactivated to return in
        # order to prevent a race condition.
        self._wait_for_volume_deactivation(name)
Пример #21
0
 def check_valid_device(self, path, *args, **kwargs):
     try:
         with open(path, 'r') as dev:
             dev.read(1)
     except IOError:
         LOG.exception(
             _LE("Failed to access the device on the path "
                 "%(path)s"), {"path": path})
         return False
     return True
Пример #22
0
 def _log_cli_err(self, err):
     """Dumps the full command output to a logfile in error cases."""
     LOG.error(
         _LE("CLI fail: '%(cmd)s' = %(code)s\nout: %(stdout)s\n"
             "err: %(stderr)s"), {
                 'cmd': err.cmd,
                 'code': err.exit_code,
                 'stdout': err.stdout,
                 'stderr': err.stderr
             })
Пример #23
0
 def extend_volume(self, lv_name, new_size):
     """Extend the size of an existing volume."""
     # Volumes with snaps have attributes 'o' or 'O' and will be
     # deactivated, but Thin Volumes with snaps have attribute 'V'
     # and won't be deactivated because the lv_has_snapshot method looks
     # for 'o' or 'O'
     if self.lv_has_snapshot(lv_name):
         self.deactivate_lv(lv_name)
     try:
         cmd = LVM.LVM_CMD_PREFIX + ['lvextend', '-L', new_size,
                                     '%s/%s' % (self.vg_name, lv_name)]
         self._execute(*cmd, root_helper=self._root_helper,
                       run_as_root=True)
     except putils.ProcessExecutionError as err:
         LOG.exception(_LE('Error extending Volume'))
         LOG.error(_LE('Cmd     :%s'), err.cmd)
         LOG.error(_LE('StdOut  :%s'), err.stdout)
         LOG.error(_LE('StdErr  :%s'), err.stderr)
         raise
Пример #24
0
    def connect_volume(self, connection_properties):
        volume_connected = False
        for (initiator_name, target_portal, target_iqn,
             target_lun) in self._get_all_paths(connection_properties):
            try:
                msg = _LI("Attempting to establish an iSCSI session to "
                          "target %(target_iqn)s on portal %(target_portal)s "
                          "accessing LUN %(target_lun)s using initiator "
                          "%(initiator_name)s.")
                LOG.info(
                    msg,
                    dict(target_portal=target_portal,
                         target_iqn=target_iqn,
                         target_lun=target_lun,
                         initiator_name=initiator_name))
                self._iscsi_utils.login_storage_target(
                    target_lun=target_lun,
                    target_iqn=target_iqn,
                    target_portal=target_portal,
                    auth_username=connection_properties.get('auth_username'),
                    auth_password=connection_properties.get('auth_password'),
                    mpio_enabled=self.use_multipath,
                    initiator_name=initiator_name,
                    ensure_lun_available=False)
                self._iscsi_utils.ensure_lun_available(
                    target_iqn=target_iqn,
                    target_lun=target_lun,
                    rescan_attempts=self.device_scan_attempts,
                    retry_interval=self.device_scan_interval)

                if not volume_connected:
                    (device_number, device_path) = (
                        self._iscsi_utils.get_device_number_and_path(
                            target_iqn, target_lun))
                    volume_connected = True

                if not self.use_multipath:
                    break
            except os_win_exc.OSWinException:
                LOG.exception(_LE("Could not establish the iSCSI session."))

        if not volume_connected:
            raise exception.BrickException(
                _("Could not connect volume %s.") % connection_properties)

        scsi_wwn = self._get_scsi_wwn(device_number)

        device_info = {
            'type': 'block',
            'path': device_path,
            'number': device_number,
            'scsi_wwn': scsi_wwn
        }
        return device_info
Пример #25
0
    def activate_lv(self, name, is_snapshot=False, permanent=False):
        """Ensure that logical volume/snapshot logical volume is activated.

        :param name: Name of LV to activate
        :param is_snapshot: whether LV is a snapshot
        :param permanent: whether we should drop skipactivation flag
        :raises: putils.ProcessExecutionError
        """

        # This is a no-op if requested for a snapshot on a version
        # of LVM that doesn't support snapshot activation.
        # (Assume snapshot LV is always active.)
        if is_snapshot and not self.supports_snapshot_lv_activation:
            return

        lv_path = self.vg_name + '/' + self._mangle_lv_name(name)

        # Must pass --yes to activate both the snap LV and its origin LV.
        # Otherwise lvchange asks if you would like to do this interactively,
        # and fails.
        cmd = ['lvchange', '-a', 'y', '--yes']

        if self.supports_lvchange_ignoreskipactivation:
            cmd.append('-K')
            # If permanent=True is specified, drop the skipactivation flag in
            # order to make this LV automatically activated after next reboot.
            if permanent:
                cmd += ['-k', 'n']

        cmd.append(lv_path)

        try:
            self._execute(*cmd,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error activating LV'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise
Пример #26
0
    def activate_lv(self, name, is_snapshot=False, permanent=False):
        """Ensure that logical volume/snapshot logical volume is activated.

        :param name: Name of LV to activate
        :param is_snapshot: whether LV is a snapshot
        :param permanent: whether we should drop skipactivation flag
        :raises: putils.ProcessExecutionError
        """

        # This is a no-op if requested for a snapshot on a version
        # of LVM that doesn't support snapshot activation.
        # (Assume snapshot LV is always active.)
        if is_snapshot and not self.supports_snapshot_lv_activation:
            return

        lv_path = self.vg_name + '/' + self._mangle_lv_name(name)

        # Must pass --yes to activate both the snap LV and its origin LV.
        # Otherwise lvchange asks if you would like to do this interactively,
        # and fails.
        cmd = ['lvchange', '-a', 'y', '--yes']

        if self.supports_lvchange_ignoreskipactivation:
            cmd.append('-K')
            # If permanent=True is specified, drop the skipactivation flag in
            # order to make this LV automatically activated after next reboot.
            if permanent:
                cmd += ['-k', 'n']

        cmd.append(lv_path)

        try:
            self._execute(*cmd,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error activating LV'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise
Пример #27
0
    def create_volume(self, name, size_str, lv_type='default', mirror_count=0):
        """Creates a logical volume on the object's VG.

        :param name: Name to use when creating Logical Volume
        :param size_str: Size to use when creating Logical Volume
        :param lv_type: Type of Volume (default or thin)
        :param mirror_count: Use LVM mirroring with specified count

        """

        if lv_type == 'thin':
            pool_path = '%s/%s' % (self.vg_name, self.vg_thin_pool)
            cmd = LVM.LVM_CMD_PREFIX + [
                'lvcreate', '-T', '-V', size_str, '-n', name, pool_path
            ]
        else:
            cmd = LVM.LVM_CMD_PREFIX + [
                'lvcreate', '-n', name, self.vg_name, '-L', size_str
            ]

        if mirror_count > 0:
            cmd.extend(
                ['-m', mirror_count, '--nosync', '--mirrorlog', 'mirrored'])
            terras = int(size_str[:-1]) / 1024.0
            if terras >= 1.5:
                rsize = int(2**math.ceil(math.log(terras) / math.log(2)))
                # NOTE(vish): Next power of two for region size. See:
                #             http://red.ht/U2BPOD
                cmd.extend(['-R', str(rsize)])

        try:
            self._execute(*cmd,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error creating Volume'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise
Пример #28
0
 def extend_volume(self, lv_name, new_size):
     """Extend the size of an existing volume."""
     # Volumes with snaps have attributes 'o' or 'O' and will be
     # deactivated, but Thin Volumes with snaps have attribute 'V'
     # and won't be deactivated because the lv_has_snapshot method looks
     # for 'o' or 'O'
     if self.lv_has_snapshot(lv_name):
         self.deactivate_lv(lv_name)
     try:
         cmd = LVM.LVM_CMD_PREFIX + [
             'lvextend', '-L', new_size,
             '%s/%s' % (self.vg_name, lv_name)
         ]
         self._execute(*cmd,
                       root_helper=self._root_helper,
                       run_as_root=True)
     except putils.ProcessExecutionError as err:
         LOG.exception(_LE('Error extending Volume'))
         LOG.error(_LE('Cmd     :%s'), err.cmd)
         LOG.error(_LE('StdOut  :%s'), err.stdout)
         LOG.error(_LE('StdErr  :%s'), err.stderr)
         raise
Пример #29
0
 def check_multipath_support(enforce_multipath):
     hostutils = utilsfactory.get_hostutils()
     mpio_enabled = hostutils.check_server_feature(hostutils.FEATURE_MPIO)
     if not mpio_enabled:
         err_msg = _LE(
             "Using multipath connections for iSCSI and FC disks "
             "requires the Multipath IO Windows feature to be "
             "enabled. MPIO must be configured to claim such devices.")
         LOG.error(err_msg)
         if enforce_multipath:
             raise exception.BrickException(err_msg)
         return False
     return True
Пример #30
0
    def is_multipath_running(enforce_multipath, root_helper):
        try:
            priv_rootwrap.execute('multipathd', 'show', 'status',
                                  run_as_root=True,
                                  root_helper=root_helper)
        except putils.ProcessExecutionError as err:
            LOG.error(_LE('multipathd is not running: exit code %(err)s'),
                      {'err': err.exit_code})
            if enforce_multipath:
                raise
            return False

        return True
Пример #31
0
    def create_volume(self, name, size_str, lv_type='default', mirror_count=0):
        """Creates a logical volume on the object's VG.

        :param name: Name to use when creating Logical Volume
        :param size_str: Size to use when creating Logical Volume
        :param lv_type: Type of Volume (default or thin)
        :param mirror_count: Use LVM mirroring with specified count

        """

        if lv_type == 'thin':
            pool_path = '%s/%s' % (self.vg_name, self.vg_thin_pool)
            cmd = LVM.LVM_CMD_PREFIX + ['lvcreate', '-T', '-V', size_str, '-n',
                                        name, pool_path]
        else:
            cmd = LVM.LVM_CMD_PREFIX + ['lvcreate', '-n', name, self.vg_name,
                                        '-L', size_str]

        if mirror_count > 0:
            cmd.extend(['-m', mirror_count, '--nosync',
                        '--mirrorlog', 'mirrored'])
            terras = int(size_str[:-1]) / 1024.0
            if terras >= 1.5:
                rsize = int(2 ** math.ceil(math.log(terras) / math.log(2)))
                # NOTE(vish): Next power of two for region size. See:
                #             http://red.ht/U2BPOD
                cmd.extend(['-R', str(rsize)])

        try:
            self._execute(*cmd,
                          root_helper=self._root_helper,
                          run_as_root=True)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error creating Volume'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)
            raise
Пример #32
0
    def _discover_iscsi_portals(self, connection_properties):
        if all([
                key in connection_properties
                for key in ('target_portals', 'target_iqns')
        ]):
            # Use targets specified by connection_properties
            return zip(connection_properties['target_portals'],
                       connection_properties['target_iqns'])

        out = None
        iscsi_transport = ('iser'
                           if self._get_transport() == 'iser' else 'default')
        if connection_properties.get('discovery_auth_method'):
            try:
                self._run_iscsiadm_update_discoverydb(connection_properties,
                                                      iscsi_transport)
            except putils.ProcessExecutionError as exception:
                # iscsiadm returns 6 for "db record not found"
                if exception.exit_code == 6:
                    # Create a new record for this target and update the db
                    self._run_iscsiadm_bare([
                        '-m', 'discoverydb', '-t', 'sendtargets', '-p',
                        connection_properties['target_portal'], '-I',
                        iscsi_transport, '--op', 'new'
                    ],
                                            check_exit_code=[0, 255])
                    self._run_iscsiadm_update_discoverydb(
                        connection_properties)
                else:
                    LOG.error(
                        _LE("Unable to find target portal: "
                            "%(target_portal)s."), {
                                'target_portal':
                                connection_properties['target_portal']
                            })
                    raise
            out = self._run_iscsiadm_bare([
                '-m', 'discoverydb', '-t', 'sendtargets', '-I',
                iscsi_transport, '-p', connection_properties['target_portal'],
                '--discover'
            ],
                                          check_exit_code=[0, 255])[0] or ""
        else:
            out = self._run_iscsiadm_bare([
                '-m', 'discovery', '-t', 'sendtargets', '-I', iscsi_transport,
                '-p', connection_properties['target_portal']
            ],
                                          check_exit_code=[0, 255])[0] or ""

        return self._get_target_portals_from_iscsiadm_output(out)
Пример #33
0
    def _discover_iscsi_portals(self, connection_properties):
        if all([key in connection_properties for key in ('target_portals',
                                                         'target_iqns')]):
            # Use targets specified by connection_properties
            return zip(connection_properties['target_portals'],
                       connection_properties['target_iqns'])

        out = None
        iscsi_transport = ('iser' if self._get_transport() == 'iser'
                           else 'default')
        if connection_properties.get('discovery_auth_method'):
            try:
                self._run_iscsiadm_update_discoverydb(connection_properties,
                                                      iscsi_transport)
            except putils.ProcessExecutionError as exception:
                # iscsiadm returns 6 for "db record not found"
                if exception.exit_code == 6:
                    # Create a new record for this target and update the db
                    self._run_iscsiadm_bare(
                        ['-m', 'discoverydb',
                         '-t', 'sendtargets',
                         '-p', connection_properties['target_portal'],
                         '-I', iscsi_transport,
                         '--op', 'new'],
                        check_exit_code=[0, 255])
                    self._run_iscsiadm_update_discoverydb(
                        connection_properties
                    )
                else:
                    LOG.error(_LE("Unable to find target portal: "
                                  "%(target_portal)s."),
                              {'target_portal': connection_properties[
                                  'target_portal']})
                    raise
            out = self._run_iscsiadm_bare(
                ['-m', 'discoverydb',
                 '-t', 'sendtargets',
                 '-I', iscsi_transport,
                 '-p', connection_properties['target_portal'],
                 '--discover'],
                check_exit_code=[0, 255])[0] or ""
        else:
            out = self._run_iscsiadm_bare(
                ['-m', 'discovery',
                 '-t', 'sendtargets',
                 '-I', iscsi_transport,
                 '-p', connection_properties['target_portal']],
                check_exit_code=[0, 255])[0] or ""

        return self._get_target_portals_from_iscsiadm_output(out)
Пример #34
0
 def __init__(self, root_helper, driver=None,
              *args, **kwargs):
     self.cli_path = os.getenv('HUAWEISDSHYPERVISORCLI_PATH')
     if not self.cli_path:
         self.cli_path = '/usr/local/bin/sds/sds_cli'
         LOG.debug("CLI path is not configured, using default %s.",
                   self.cli_path)
     if not os.path.isfile(self.cli_path):
         self.iscliexist = False
         LOG.error(_LE('SDS CLI file not found, '
                       'HuaweiStorHyperConnector init failed.'))
     super(HuaweiStorHyperConnector, self).__init__(root_helper,
                                                    driver=driver,
                                                    *args, **kwargs)
Пример #35
0
    def connect_volume(self, connection_properties):
        """Connect to a volume.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes;
                                      it needs to contain the StorPool
                                      'client_id' and the common 'volume' and
                                      'access_mode' values.
        :type connection_properties: dict
        :returns: dict
        """
        client_id = connection_properties.get('client_id', None)
        if client_id is None:
            raise exception.BrickException(
                _LE('Invalid StorPool connection data, no client ID specified.'
                    ))
        volume_id = connection_properties.get('volume', None)
        if volume_id is None:
            raise exception.BrickException(
                _LE('Invalid StorPool connection data, no volume ID specified.'
                    ))
        volume = self._attach.volumeName(volume_id)
        mode = connection_properties.get('access_mode', None)
        if mode is None or mode not in ('rw', 'ro'):
            raise exception.BrickException(
                _LE('Invalid access_mode specified in the connection data.'))
        req_id = 'brick-%s-%s' % (client_id, volume_id)
        self._attach.add(
            req_id, {
                'volume': volume,
                'type': 'brick',
                'id': req_id,
                'rights': 1 if mode == 'ro' else 2,
                'volsnap': False
            })
        self._attach.sync(req_id, None)
        return {'type': 'block', 'path': '/dev/storpool/' + volume}
Пример #36
0
    def _get_thin_pool_free_space(self, vg_name, thin_pool_name):
        """Returns available thin pool free space.

        :param vg_name: the vg where the pool is placed
        :param thin_pool_name: the thin pool to gather info for
        :returns: Free space in GB (float), calculated using data_percent

        """
        cmd = LVM.LVM_CMD_PREFIX + [
            'lvs', '--noheadings', '--unit=g', '-o', 'size,data_percent',
            '--separator', ':', '--nosuffix'
        ]
        # NOTE(gfidente): data_percent only applies to some types of LV so we
        # make sure to append the actual thin pool name
        cmd.append("/dev/%s/%s" % (vg_name, thin_pool_name))

        free_space = 0.0

        try:
            (out, err) = self._execute(*cmd,
                                       root_helper=self._root_helper,
                                       run_as_root=True)
            if out is not None:
                out = out.strip()
                data = out.split(':')
                pool_size = float(data[0])
                data_percent = float(data[1])
                consumed_space = pool_size / 100 * data_percent
                free_space = pool_size - consumed_space
                free_space = round(free_space, 2)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error querying thin pool about data_percent'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)

        return free_space
    def __init__(self, client, name, snapshot=None, read_only=False):
        if snapshot is not None:
            snapshot = encodeutils.safe_encode(snapshot)

        try:
            self.image = client.rbd.Image(client.ioctx,
                                          encodeutils.safe_encode(name),
                                          snapshot=snapshot,
                                          read_only=read_only)
        except client.rbd.Error:
            LOG.exception(_LE("error opening rbd image %s"), name)
            client.disconnect()
            raise

        self.client = client
Пример #38
0
    def _get_thin_pool_free_space(self, vg_name, thin_pool_name):
        """Returns available thin pool free space.

        :param vg_name: the vg where the pool is placed
        :param thin_pool_name: the thin pool to gather info for
        :returns: Free space in GB (float), calculated using data_percent

        """
        cmd = LVM.LVM_CMD_PREFIX + ['lvs', '--noheadings', '--unit=g',
                                    '-o', 'size,data_percent', '--separator',
                                    ':', '--nosuffix']
        # NOTE(gfidente): data_percent only applies to some types of LV so we
        # make sure to append the actual thin pool name
        cmd.append("/dev/%s/%s" % (vg_name, thin_pool_name))

        free_space = 0.0

        try:
            (out, err) = self._execute(*cmd,
                                       root_helper=self._root_helper,
                                       run_as_root=True)
            if out is not None:
                out = out.strip()
                data = out.split(':')
                pool_size = float(data[0])
                data_percent = float(data[1])
                consumed_space = pool_size / 100 * data_percent
                free_space = pool_size - consumed_space
                free_space = round(free_space, 2)
        except putils.ProcessExecutionError as err:
            LOG.exception(_LE('Error querying thin pool about data_percent'))
            LOG.error(_LE('Cmd     :%s'), err.cmd)
            LOG.error(_LE('StdOut  :%s'), err.stdout)
            LOG.error(_LE('StdErr  :%s'), err.stderr)

        return free_space
Пример #39
0
    def __init__(self, client, name, snapshot=None, read_only=False):
        if snapshot is not None:
            snapshot = utils.convert_str(snapshot)

        try:
            self.image = client.rbd.Image(client.ioctx,
                                          utils.convert_str(name),
                                          snapshot=snapshot,
                                          read_only=read_only)
        except client.rbd.Error:
            LOG.exception(_LE("error opening rbd image %s"), name)
            client.disconnect()
            raise

        self.client = client
Пример #40
0
 def __init__(self, root_helper, driver=None, *args, **kwargs):
     self.cli_path = os.getenv('HUAWEISDSHYPERVISORCLI_PATH')
     if not self.cli_path:
         self.cli_path = '/usr/local/bin/sds/sds_cli'
         LOG.debug("CLI path is not configured, using default %s.",
                   self.cli_path)
     if not os.path.isfile(self.cli_path):
         self.iscliexist = False
         LOG.error(
             _LE('SDS CLI file not found, '
                 'HuaweiStorHyperConnector init failed.'))
     super(HuaweiStorHyperConnector, self).__init__(root_helper,
                                                    driver=driver,
                                                    *args,
                                                    **kwargs)
Пример #41
0
def get_volume_encryptor(root_helper,
                         connection_info,
                         keymgr,
                         execute=None,
                         *args, **kwargs):
    """Creates a VolumeEncryptor used to encrypt the specified volume.

    :param: the connection information used to attach the volume
    :returns VolumeEncryptor: the VolumeEncryptor for the volume
    """
    encryptor = nop.NoOpEncryptor(root_helper=root_helper,
                                  connection_info=connection_info,
                                  keymgr=keymgr,
                                  execute=execute,
                                  *args, **kwargs)

    location = kwargs.get('control_location', None)
    if location and location.lower() == 'front-end':  # case insensitive
        provider = kwargs.get('provider')

        if provider == 'LuksEncryptor' or 'LuksEncryptor' in provider:
            provider = 'os_brick.encryptors.luks.LuksEncryptor'
        elif (provider == 'CryptsetupEncryptor' or
              'CryptsetupEncryptor' in provider):
            provider = \
                'os_brick.encryptors.cryptsetup.CryptsetupEncryptor'
        elif (provider == 'NoOpEncryptor' or 'NoOpEncryptor' in provider):
            provider = 'os_brick.encryptors.nop.NoOpEncryptor'

        try:
            encryptor = importutils.import_object(
                provider,
                root_helper,
                connection_info,
                keymgr,
                execute,
                **kwargs)
        except Exception as e:
            LOG.error(_LE("Error instantiating %(provider)s: %(exception)s"),
                      {'provider': provider, 'exception': e})
            raise

    msg = ("Using volume encryptor '%(encryptor)s' for connection: "
           "%(connection_info)s" %
           {'encryptor': encryptor, 'connection_info': connection_info})
    LOG.debug(strutils.mask_password(msg))

    return encryptor
Пример #42
0
    def connect_volume(self, connection_properties):
        volume_connected = False
        for (initiator_name,
             target_portal,
             target_iqn,
             target_lun) in self._get_all_paths(connection_properties):
            try:
                msg = _LI("Attempting to establish an iSCSI session to "
                          "target %(target_iqn)s on portal %(target_portal)s "
                          "accessing LUN %(target_lun)s using initiator "
                          "%(initiator_name)s.")
                LOG.info(msg, dict(target_portal=target_portal,
                                   target_iqn=target_iqn,
                                   target_lun=target_lun,
                                   initiator_name=initiator_name))
                self._iscsi_utils.login_storage_target(
                    target_lun=target_lun,
                    target_iqn=target_iqn,
                    target_portal=target_portal,
                    auth_username=connection_properties.get('auth_username'),
                    auth_password=connection_properties.get('auth_password'),
                    mpio_enabled=self.use_multipath,
                    initiator_name=initiator_name,
                    rescan_attempts=self.device_scan_attempts)

                if not volume_connected:
                    (device_number,
                     device_path) = (
                        self._iscsi_utils.get_device_number_and_path(
                            target_iqn, target_lun))
                    volume_connected = True

                if not self.use_multipath:
                    break
            except os_win_exc.OSWinException:
                LOG.exception(_LE("Could not establish the iSCSI session."))

        if not volume_connected:
            raise exception.BrickException(
                _("Could not connect volume %s.") % connection_properties)

        scsi_wwn = self._get_scsi_wwn(device_number)

        device_info = {'type': 'block',
                       'path': device_path,
                       'number': device_number,
                       'scsi_wwn': scsi_wwn}
        return device_info
Пример #43
0
 def check_valid_device(self, path, run_as_root=True):
     cmd = ('dd', 'if=%(path)s' % {"path": path},
            'of=/dev/null', 'count=1')
     out, info = None, None
     try:
         out, info = self._execute(*cmd, run_as_root=run_as_root,
                                   root_helper=self._root_helper)
     except putils.ProcessExecutionError as e:
         LOG.error(_LE("Failed to access the device on the path "
                       "%(path)s: %(error)s."),
                   {"path": path, "error": e.stderr})
         return False
     # If the info is none, the path does not exist.
     if info is None:
         return False
     return True
Пример #44
0
 def _cli_cmd(self, method, volume_name):
     LOG.debug("Enter into _cli_cmd.")
     if not self.iscliexist:
         msg = _("SDS command line doesn't exist, "
                 "can't execute SDS command.")
         raise exception.BrickException(message=msg)
     if not method or volume_name is None:
         return
     cmd = [self.cli_path, '-c', method, '-v', volume_name]
     out, clilog = self._execute(*cmd, run_as_root=False,
                                 root_helper=self._root_helper)
     analyse_result = self._analyze_output(out)
     LOG.debug('%(method)s volume returns %(analyse_result)s.',
               {'method': method, 'analyse_result': analyse_result})
     if clilog:
         LOG.error(_LE("SDS CLI output some log: %s."), clilog)
     return analyse_result
Пример #45
0
    def disconnect_volume(self, connection_properties, device_info):
        """Detach the volume from instance_name.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes.
        :type connection_properties: dict
        :param device_info: historical difference, but same as connection_props
        :type device_info: dict

        connection_properties for iSCSI must include:
        target_portal(s) - IP and optional port
        target_iqn(s) - iSCSI Qualified Name
        target_lun(s) - LUN id of the volume
        """
        if self.use_multipath:
            self._rescan_multipath()
            host_device = multipath_device = None
            host_devices = self._get_device_path(connection_properties)
            # Choose an accessible host device
            for dev in host_devices:
                if os.path.exists(dev):
                    host_device = dev
                    device_wwn = self._linuxscsi.get_scsi_wwn(dev)
                    (multipath_device, multipath_id) = (super(
                        ISCSIConnector,
                        self)._discover_mpath_device(device_wwn,
                                                     connection_properties,
                                                     dev))
                    if multipath_device:
                        break
            if not host_device:
                LOG.error(_LE("No accessible volume device: %(host_devices)s"),
                          {'host_devices': host_devices})
                raise exception.VolumeDeviceNotFound(device=host_devices)

            if multipath_device:
                device_realpath = os.path.realpath(host_device)
                self._linuxscsi.remove_multipath_device(device_realpath)
                return self._disconnect_volume_multipath_iscsi(
                    connection_properties, multipath_device)

        # When multiple portals/iqns/luns are specified, we need to remove
        # unused devices created by logging into other LUNs' session.
        for props in self._iterate_all_targets(connection_properties):
            self._disconnect_volume_iscsi(props)
Пример #46
0
    def disconnect_volume(self, connection_properties, device_info):
        """Detach the volume from instance_name.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes.
        :type connection_properties: dict
        :param device_info: historical difference, but same as connection_props
        :type device_info: dict

        connection_properties for iSCSI must include:
        target_portal(s) - IP and optional port
        target_iqn(s) - iSCSI Qualified Name
        target_lun(s) - LUN id of the volume
        """
        if self.use_multipath:
            self._rescan_multipath()
            host_device = multipath_device = None
            host_devices = self._get_device_path(connection_properties)
            # Choose an accessible host device
            for dev in host_devices:
                if os.path.exists(dev):
                    host_device = dev
                    device_wwn = self._linuxscsi.get_scsi_wwn(dev)
                    (multipath_device, multipath_id) = (super(
                        ISCSIConnector, self)._discover_mpath_device(
                            device_wwn, connection_properties, dev))
                    if multipath_device:
                        break
            if not host_device:
                LOG.error(_LE("No accessible volume device: %(host_devices)s"),
                          {'host_devices': host_devices})
                raise exception.VolumeDeviceNotFound(device=host_devices)

            if multipath_device:
                device_realpath = os.path.realpath(host_device)
                self._linuxscsi.remove_multipath_device(device_realpath)
                return self._disconnect_volume_multipath_iscsi(
                    connection_properties, multipath_device)

        # When multiple portals/iqns/luns are specified, we need to remove
        # unused devices created by logging into other LUNs' session.
        for props in self._iterate_all_targets(connection_properties):
            self._disconnect_volume_iscsi(props)
Пример #47
0
    def check_valid_device(self, path, run_as_root=True):
        """Verify an existing RBD handle is connected and valid."""
        rbd_handle = path

        if rbd_handle is None:
            return False

        original_offset = rbd_handle.tell()

        try:
            rbd_handle.read(4096)
        except Exception as e:
            LOG.error(_LE("Failed to access RBD device handle: %(error)s"),
                      {"error": e})
            return False
        finally:
            rbd_handle.seek(original_offset, 0)

        return True
Пример #48
0
 def check_valid_device(self, path, run_as_root=True):
     cmd = ('dd', 'if=%(path)s' % {"path": path}, 'of=/dev/null', 'count=1')
     out, info = None, None
     try:
         out, info = self._execute(*cmd,
                                   run_as_root=run_as_root,
                                   root_helper=self._root_helper)
     except putils.ProcessExecutionError as e:
         LOG.error(
             _LE("Failed to access the device on the path "
                 "%(path)s: %(error)s."), {
                     "path": path,
                     "error": e.stderr
                 })
         return False
     # If the info is none, the path does not exist.
     if info is None:
         return False
     return True
Пример #49
0
    def check_valid_device(self, path, run_as_root=True):
        """Verify an existing RBD handle is connected and valid."""
        rbd_handle = path

        if rbd_handle is None:
            return False

        original_offset = rbd_handle.tell()

        try:
            rbd_handle.read(4096)
        except Exception as e:
            LOG.error(_LE("Failed to access RBD device handle: %(error)s"),
                      {"error": e})
            return False
        finally:
            rbd_handle.seek(original_offset, 0)

        return True
Пример #50
0
    def create_thin_pool(self, name=None):
        """Creates a thin provisioning pool for this VG.

        The syntax here is slightly different than the default
        lvcreate -T, so we'll just write a custom cmd here
        and do it.

        :param name: Name to use for pool, default is "<vg-name>-pool"
        :returns: The size string passed to the lvcreate command

        """

        if not LVM.supports_thin_provisioning(self._root_helper):
            LOG.error(
                _LE('Requested to setup thin provisioning, '
                    'however current LVM version does not '
                    'support it.'))
            return None

        if name is None:
            name = '%s-pool' % self.vg_name

        vg_pool_name = '%s/%s' % (self.vg_name, name)

        size_args = self._calculate_thin_pool_size()

        cmd = LVM.LVM_CMD_PREFIX + ['lvcreate', '-T']
        cmd.extend(size_args)
        cmd.append(vg_pool_name)

        LOG.debug(
            "Creating thin pool '%(pool)s' with size %(size)s of "
            "total %(free)sg", {
                'pool': vg_pool_name,
                'size': size_args,
                'free': self.vg_free_space
            })

        self._execute(*cmd, root_helper=self._root_helper, run_as_root=True)

        self.vg_thin_pool = name

        return
Пример #51
0
 def _cli_cmd(self, method, volume_name):
     LOG.debug("Enter into _cli_cmd.")
     if not self.iscliexist:
         msg = _("SDS command line doesn't exist, "
                 "can't execute SDS command.")
         raise exception.BrickException(message=msg)
     if not method or volume_name is None:
         return
     cmd = [self.cli_path, '-c', method, '-v', volume_name]
     out, clilog = self._execute(*cmd,
                                 run_as_root=False,
                                 root_helper=self._root_helper)
     analyse_result = self._analyze_output(out)
     LOG.debug('%(method)s volume returns %(analyse_result)s.', {
         'method': method,
         'analyse_result': analyse_result
     })
     if clilog:
         LOG.error(_LE("SDS CLI output some log: %s."), clilog)
     return analyse_result
Пример #52
0
    def create_thin_pool(self, name=None):
        """Creates a thin provisioning pool for this VG.

        The syntax here is slightly different than the default
        lvcreate -T, so we'll just write a custom cmd here
        and do it.

        :param name: Name to use for pool, default is "<vg-name>-pool"
        :returns: The size string passed to the lvcreate command

        """

        if not LVM.supports_thin_provisioning(self._root_helper):
            LOG.error(_LE('Requested to setup thin provisioning, '
                          'however current LVM version does not '
                          'support it.'))
            return None

        if name is None:
            name = '%s-pool' % self.vg_name

        vg_pool_name = '%s/%s' % (self.vg_name, name)

        size_args = self._calculate_thin_pool_size()

        cmd = LVM.LVM_CMD_PREFIX + ['lvcreate', '-T']
        cmd.extend(size_args)
        cmd.append(vg_pool_name)

        LOG.debug("Creating thin pool '%(pool)s' with size %(size)s of "
                  "total %(free)sg", {'pool': vg_pool_name,
                                      'size': size_args,
                                      'free': self.vg_free_space})

        self._execute(*cmd,
                      root_helper=self._root_helper,
                      run_as_root=True)

        self.vg_thin_pool = name

        return
Пример #53
0
    def _get_hba_channel_scsi_target(self, hba):
        """Try to get the HBA channel and SCSI target for an HBA.

        This method only works for Fibre Channel targets that implement a
        single WWNN for all ports, so caller should expect us to return either
        None or an empty list.
        """
        # Leave only the number from the host_device field (ie: host6)
        host_device = hba['host_device']
        if host_device and len(host_device) > 4:
            host_device = host_device[4:]

        path = '/sys/class/fc_transport/target%s:' % host_device
        cmd = 'grep %(wwnn)s %(path)s*/node_name' % {'wwnn': hba['node_name'],
                                                     'path': path}
        try:
            out, _err = self._execute(cmd)
            return [line.split('/')[4].split(':')[1:]
                    for line in out.split('\n') if line.startswith(path)]
        except Exception as exc:
            LOG.error(_LE('Could not get HBA channel and SCSI target ID, '
                          'reason: %s'), exc)
            return None
Пример #54
0
        def _wait_for_device_discovery(host_devices):
            tries = self.tries
            for device in host_devices:
                LOG.debug("Looking for Fibre Channel dev %(device)s",
                          {'device': device})
                if os.path.exists(device):
                    self.host_device = device
                    # get the /dev/sdX device.  This is used
                    # to find the multipath device.
                    self.device_name = os.path.realpath(device)
                    raise loopingcall.LoopingCallDone()

            if self.tries >= self.device_scan_attempts:
                LOG.error(_LE("Fibre Channel volume device not found."))
                raise exception.NoFibreChannelVolumeDeviceFound()

            LOG.info(_LI("Fibre Channel volume device not yet found. "
                         "Will rescan & retry.  Try number: %(tries)s."),
                     {'tries': tries})

            self._linuxfc.rescan_hosts(hbas,
                                       connection_properties['target_lun'])
            self.tries = self.tries + 1
    def extend_volume(self, connection_properties):
        """Update the attached volume's size.

        This method will attempt to update the local hosts's
        volume after the volume has been extended on the remote
        system.  The new volume size in bytes will be returned.
        If there is a failure to update, then None will be returned.

        :param connection_properties: The volume connection properties.
        :returns: new size of the volume.
        """
        # The StorPool client (storpool_block service) running on this host
        # should have picked up the change already, so it is enough to query
        # the actual disk device to see if its size is correct.
        #
        # TODO(pp): query the API to see if this is really the case
        volume_id = connection_properties.get('volume', None)
        if volume_id is None:
            raise exception.BrickException(_LE(
                'Invalid StorPool connection data, no volume ID specified.'))
        volume = self._attach.volumeName(volume_id)
        path = '/dev/storpool/' + volume
        return self._get_device_size(path)
Пример #56
0
def get_encryption_metadata(context, volume_api, volume_id, connection_info):
    metadata = {}
    if ('data' in connection_info and
            connection_info['data'].get('encrypted', False)):
        try:
            metadata = volume_api.get_volume_encryption_metadata(context,
                                                                 volume_id)
            if not metadata:
                LOG.warning(_LW(
                    'Volume %s should be encrypted but there is no '
                    'encryption metadata.'), volume_id)
        except Exception as e:
            LOG.error(_LE("Failed to retrieve encryption metadata for "
                          "volume %(volume_id)s: %(exception)s"),
                      {'volume_id': volume_id, 'exception': e})
            raise

    if metadata:
        msg = ("Using volume encryption metadata '%(metadata)s' for "
               "connection: %(connection_info)s" %
               {'metadata': metadata, 'connection_info': connection_info})
        LOG.debug(strutils.mask_password(msg))

    return metadata
Пример #57
0
    def __init__(self, vg_name, root_helper, create_vg=False,
                 physical_volumes=None, lvm_type='default',
                 executor=putils.execute, lvm_conf=None):

        """Initialize the LVM object.

        The LVM object is based on an LVM VolumeGroup, one instantiation
        for each VolumeGroup you have/use.

        :param vg_name: Name of existing VG or VG to create
        :param root_helper: Execution root_helper method to use
        :param create_vg: Indicates the VG doesn't exist
                          and we want to create it
        :param physical_volumes: List of PVs to build VG on
        :param lvm_type: VG and Volume type (default, or thin)
        :param executor: Execute method to use, None uses
                         oslo_concurrency.processutils

        """
        super(LVM, self).__init__(execute=executor, root_helper=root_helper)
        self.vg_name = vg_name
        self.pv_list = []
        self.vg_size = 0.0
        self.vg_free_space = 0.0
        self.vg_lv_count = 0
        self.vg_uuid = None
        self.vg_thin_pool = None
        self.vg_thin_pool_size = 0.0
        self.vg_thin_pool_free_space = 0.0
        self._supports_snapshot_lv_activation = None
        self._supports_lvchange_ignoreskipactivation = None
        self.vg_provisioned_capacity = 0.0

        # Ensure LVM_SYSTEM_DIR has been added to LVM.LVM_CMD_PREFIX
        # before the first LVM command is executed, and use the directory
        # where the specified lvm_conf file is located as the value.
        if lvm_conf and os.path.isfile(lvm_conf):
            lvm_sys_dir = os.path.dirname(lvm_conf)
            LVM.LVM_CMD_PREFIX = ['env',
                                  'LC_ALL=C',
                                  'LVM_SYSTEM_DIR=' + lvm_sys_dir]

        if create_vg and physical_volumes is not None:
            self.pv_list = physical_volumes

            try:
                self._create_vg(physical_volumes)
            except putils.ProcessExecutionError as err:
                LOG.exception(_LE('Error creating Volume Group'))
                LOG.error(_LE('Cmd     :%s'), err.cmd)
                LOG.error(_LE('StdOut  :%s'), err.stdout)
                LOG.error(_LE('StdErr  :%s'), err.stderr)
                raise exception.VolumeGroupCreationFailed(vg_name=self.vg_name)

        if self._vg_exists() is False:
            LOG.error(_LE('Unable to locate Volume Group %s'), vg_name)
            raise exception.VolumeGroupNotFound(vg_name=vg_name)

        # NOTE: we assume that the VG has been activated outside of Cinder

        if lvm_type == 'thin':
            pool_name = "%s-pool" % self.vg_name
            if self.get_volume(pool_name) is None:
                try:
                    self.create_thin_pool(pool_name)
                except putils.ProcessExecutionError:
                    # Maybe we just lost the race against another copy of
                    # this driver being in init in parallel - e.g.
                    # cinder-volume and cinder-backup starting in parallel
                    if self.get_volume(pool_name) is None:
                        raise

            self.vg_thin_pool = pool_name
            self.activate_lv(self.vg_thin_pool)
        self.pv_list = self.get_all_physical_volumes(root_helper, vg_name)
Пример #58
0
    def update_volume_group_info(self):
        """Update VG info for this instantiation.

        Used to update member fields of object and
        provide a dict of info for caller.

        :returns: Dictionaries of VG info

        """
        vg_list = self.get_all_volume_groups(self._root_helper, self.vg_name)

        if len(vg_list) != 1:
            LOG.error(_LE('Unable to find VG: %s'), self.vg_name)
            raise exception.VolumeGroupNotFound(vg_name=self.vg_name)

        self.vg_size = float(vg_list[0]['size'])
        self.vg_free_space = float(vg_list[0]['available'])
        self.vg_lv_count = int(vg_list[0]['lv_count'])
        self.vg_uuid = vg_list[0]['uuid']

        total_vols_size = 0.0
        if self.vg_thin_pool is not None:
            # NOTE(xyang): If providing only self.vg_name,
            # get_lv_info will output info on the thin pool and all
            # individual volumes.
            # get_lv_info(self._root_helper, 'stack-vg')
            # sudo lvs --noheadings --unit=g -o vg_name,name,size
            # --nosuffix stack-vg
            # stack-vg stack-pool               9.51
            # stack-vg volume-13380d16-54c3-4979-9d22-172082dbc1a1  1.00
            # stack-vg volume-629e13ab-7759-46a5-b155-ee1eb20ca892  1.00
            # stack-vg volume-e3e6281c-51ee-464c-b1a7-db6c0854622c  1.00
            #
            # If providing both self.vg_name and self.vg_thin_pool,
            # get_lv_info will output only info on the thin pool, but not
            # individual volumes.
            # get_lv_info(self._root_helper, 'stack-vg', 'stack-pool')
            # sudo lvs --noheadings --unit=g -o vg_name,name,size
            # --nosuffix stack-vg/stack-pool
            # stack-vg stack-pool               9.51
            #
            # We need info on both the thin pool and the volumes,
            # therefore we should provide only self.vg_name, but not
            # self.vg_thin_pool here.
            for lv in self.get_lv_info(self._root_helper,
                                       self.vg_name):
                lvsize = lv['size']
                # get_lv_info runs "lvs" command with "--nosuffix".
                # This removes "g" from "1.00g" and only outputs "1.00".
                # Running "lvs" command without "--nosuffix" will output
                # "1.00g" if "g" is the unit.
                # Remove the unit if it is in lv['size'].
                if not lv['size'][-1].isdigit():
                    lvsize = lvsize[:-1]
                if lv['name'] == self.vg_thin_pool:
                    self.vg_thin_pool_size = lvsize
                    tpfs = self._get_thin_pool_free_space(self.vg_name,
                                                          self.vg_thin_pool)
                    self.vg_thin_pool_free_space = tpfs
                else:
                    total_vols_size = total_vols_size + float(lvsize)
            total_vols_size = round(total_vols_size, 2)

        self.vg_provisioned_capacity = total_vols_size