Driver to use BeeGFS as cinder storage target

Registered by Sven Rath

This project is about developing and maintaining a driver which allows cinder to connect to the beegfs filesystem. For the moment there will be only simple features implemented to have the driver as soon as possible available and develop more features in a next step.

Blueprint information

Status:
Not started
Approver:
Sean McGinnis
Priority:
Undefined
Drafter:
Sven Rath
Direction:
Needs approval
Assignee:
Sven Rath
Definition:
Approved
Series goal:
Proposed for alpha-trunk
Implementation:
Deferred
Milestone target:
milestone icon ongoing

Related branches

Sprints

Whiteboard

####################################

First idea/draft of the driver to use beegfs

########################################

# Copyright. 2016 All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""
BeeGFS Volume Driver.

"""
import math
import os
import re
import shutil

from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import units
import six

from cinder import context
from cinder import exception
from cinder.i18n import _, _LE, _LI
from cinder.image import image_utils
from cinder import interface
from cinder.objects import fields
from cinder import utils
from cinder.volume import driver
from cinder.volume.drivers import nfs
from cinder.volume.drivers import remotefs
from cinder.volume.drivers.san import san

LOG = logging.getLogger(__name__)

beegfs_options = [
    cfg.StrOpt('beegfs_mount_point',
               help='Specifies the path of the BeeGFS directory where Block '
                    'Storage volume and snapshot files are stored.'),

    cfg.StrOpt('beegfs_images_dir',
               help='Specifies the path of the Image service repository in '
                    'BeeGFS. Leave undefined if not storing images in BeeGFS.'),
]

CONF = cfg.CONF
CONF.register_opts(beegfs_options)

@interface.volumedriver
class BeeGFSDriver(driver.VolumeDriver):

#Basic configuration

    def __init__(self, *args, **kwargs):
        super(BeeGFSDriver, self)._init_(*args,**kwargs)
        self.configuration.append_config_values(beegfs_options)
        self.hostname = socket.gethostname()
        self.beegfs_execute = self._beegfs_local_execute

    def _check_config(self):
            #Ensure that the flags we care about are set.
            required_config = ['beegfs_mount_point, beegfs_images_dir']
            for attr in required_config:
                if not getattr(self.configuration, attr, None):
                raise exception.InvalidInput(reason=_('%s is not set.') %attr)

    def _beegfs_local_execute(self, *cmd, **kwargs):
        if 'run_as_root' not in kwargs:
        kwargs.update({'run_as_root':True})
        return utils.execute(*cmd, **kwargs)
#Volumes

    def _create_volume(self, volume):
        #Now we have to verify that BeeGFS is mounted!
            self._verify_beegfs_path(self.configuration.beegfs_mount_point)
        volume_path = self._get_volume_path(volume)
        volume_size = volume['size']

    def delete_volume(self, volume):
            """Deletes a logical volume."""
            # Check if BeeGFS is mounted
            self._verify_beegfs_path(self.configuration.beegfs_mount_point)
        volume_path = self._get_volume_path(volume)
            mount_point = os.path.dirname(self.local_path(volume))

    def _create_volume_from_snapshot(self, volume, snapshot):
             snapshot_path = self._get_snapshot_path(snapshot)

#Snapshots

    def create_snapshot(self, snapshot):
            snapshot_path = self._get_snapshot_path(snapshot)
            volume_path = os.path.join(os.path.dirname(snapshot_path),snapshot.volume.name)

    def delete_snapshot(self, snapshot):
    snapshot_path = self._get_snapshot_path(snapshot)
    snapshot_ts_path = '%s.ts' % snapshot_path
    self.beegfs_execute('mv',snapshot_path,snapshot_ts_path)
    self.beegfs_execute('rm','-f',snapshot_ts_path,check_exit_code=False)

#Clone and copy

        def clone_image(self, context, volume, image_location, image_meta, image_service):
        #Create a volume from the specified image.
            return self._clone_image(volume, image_location, image_meta['id'])

    def copy_image_to_volume(self, context, volume, image_service, image_id):
            # Check if BeeGFS is mounted
            self._verify_beegfs_path(self.configuration.beegfs_mount_point)
            LOG.debug('Copy image to vol %s using image_utils fetch_to_raw.', volume['id'])
            image_utils.fetch_to_raw(context, image_service, image_id, self.local_path(volume), self.configuration.volume_dd_blocksize, size=volume['size'])
            self._resize_volume_file(volume, volume['size'])

    def copy_volume_to_image(self, context, volume, image_service, image_meta):
            #Copy the volume to the specified image.
            image_utils.upload_volume(context, image_service, image_meta, self.local_path(volume))

#Resizing

    def extend_volume(self, volume, new_size):
            #Extend an existing volume.
            self._resize_volume_file(volume, new_size)

    def _resize_volume_file(self, volume, new_size):
            #Resize volume file to new size.
            vol_path = self.local_path(volume)
            try:
             image_utils.resize_image(vol_path, new_size, run_as_root=True)
            except processutils.ProcessExecutionError as exc:
                LOG.error(_LE("Failed to resize volume "
                          "%(volume_id)s, error: %(error)s."),
                      {'volume_id': volume['id'],
                       'error': exc.stderr})
                raise exception.VolumeBackendAPIException(data=exc.stderr)
            data = image_utils.qemu_img_info(vol_path)
            return data.virtual_size

#GET methods and verification

    def _get_snapshot_path(self, snapshot):
            #Returns the BeeGFS path for the snapshot
            snap_parent_vol = self.db.volume_get(self._context, snapshot['volume_id'])
            snap_parent_vol_path = self._get_volume_path(snap_parent_vol)
            snapshot_path = os.path.join(os.path.dirname(snap_parent_vol_path), snapshot['name'])
            return snapshot_path

    def _get_volume_path(self, volume):
             return self.local_path(volume)

    def get_volume_stats(self, refresh=False):
            #If 'refresh' is True, or stats have never been updated, run update
            #the stats first.
            if not self._stats or refresh:
                self._update_volume_stats()
            return self._stats

    def _update_volume_stats(self):
            #Retrieve stats info from volume group.
            LOG.debug("Enter _update_volume_stats.")
            beegfs_base = self.configuration.beegfs_mount_point
            data = {}
            backend_name = self.configuration.safe_get('volume_backend_name')
            data['volume_backend_name'] = backend_name
            data['vendor_name'] = 'BeeGFS'
            data['driver_version'] = self.get_version()
            data['storage_protocol'] = 'file'
            self._ensure_shares_mounted()
            global_capacity = 0
            global_free = 0
            for share in self._mounted_shares:
                capacity, free, _used = self._get_capacity_info(share)
                global_capacity += capacity
                global_free += free
            data['total_capacity_gb'] = global_capacity / float(units.Gi)
            data['free_capacity_gb'] = global_free / float(units.Gi)
            data['reserved_percentage'] = 0
            data['QoS_support'] = False
            data['storage_pool'] = self._storage_pool
            #data['location_info'] =
            data['consistencygroup_support'] = 'True'
            self._stats = data
            LOG.debug("Exit _update_volume_stats.")

    def _verify_beegfs_path(self, path):
             """Examine if BeeGFS is active and file system is mounted or not."""
            try:
                self._is_beegfs_path(path)
            except processutils.ProcessExecutionError:
                msg = (_('%s cannot be accessed. Verify that BeeGFS is active and '
                     'file system is mounted.') % path)
                LOG.error(msg)
                raise exception.VolumeBackendAPIException(data=msg)

    def _is_beegfs_path(self, directory):
            """Determine if the specified path is in a beegfs file system.
            If not part of a beegfs file system, raise ProcessExecutionError.
             """
            try:
                self.beegfs_execute('mmlsattr', directory)
            except processutils.ProcessExecutionError as exc:
                LOG.error(_LE('Failed to issue ????? command '
                        'for path %(path)s, '
                        'error: %(error)s.'),
                        {'path': directory,
                       'error': exc.stderr})
                    raise exception.VolumeBackendAPIException(data=exc.stderr)

(?)

Work Items

This blueprint contains Public information 
Everyone can see this information.