components/openstack/cinder/files/zfssa/zfssaiscsi.py
changeset 3998 5bd484384122
parent 3997 0ca3f3d6c919
child 4002 95b8f35fcdd5
equal deleted inserted replaced
3997:0ca3f3d6c919 3998:5bd484384122
     1 # Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
       
     2 #
       
     3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
       
     4 #    not use this file except in compliance with the License. You may obtain
       
     5 #    a copy of the License at
       
     6 #
       
     7 #         http://www.apache.org/licenses/LICENSE-2.0
       
     8 #
       
     9 #    Unless required by applicable law or agreed to in writing, software
       
    10 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
       
    11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
       
    12 #    License for the specific language governing permissions and limitations
       
    13 #    under the License.
       
    14 """
       
    15 ZFS Storage Appliance Cinder Volume Driver
       
    16 """
       
    17 import base64
       
    18 
       
    19 from cinder import exception
       
    20 from cinder.openstack.common import log
       
    21 from cinder.volume import driver
       
    22 from oslo.config import cfg
       
    23 
       
    24 from cinder.volume.drivers.zfssa import zfssarest
       
    25 
       
    26 
       
    27 CONF = cfg.CONF
       
    28 LOG = log.getLogger(__name__)
       
    29 
       
    30 ZFSSA_OPTS = [
       
    31     cfg.StrOpt('zfssa_host', required=True,
       
    32                help='ZFSSA management IP address'),
       
    33     cfg.StrOpt('zfssa_auth_user', required=True, secret=True,
       
    34                help='ZFSSA management authorized user\'s name'),
       
    35     cfg.StrOpt('zfssa_auth_password', required=True, secret=True,
       
    36                help='ZFSSA management authorized user\'s password'),
       
    37     cfg.StrOpt('zfssa_pool', required=True,
       
    38                help='ZFSSA storage pool name'),
       
    39     cfg.StrOpt('zfssa_project', required=True,
       
    40                help='ZFSSA project name'),
       
    41     cfg.StrOpt('zfssa_lun_volblocksize', default='8k',
       
    42                help='Block size: 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, 128k'),
       
    43     cfg.BoolOpt('zfssa_lun_sparse', default=False,
       
    44                 help='Flag to enable sparse (thin-provisioned): True, False'),
       
    45     cfg.StrOpt('zfssa_lun_compression', default='',
       
    46                help='Data compression-off, lzjb, gzip-2, gzip, gzip-9'),
       
    47     cfg.StrOpt('zfssa_lun_logbias', default='',
       
    48                help='Synchronous write bias-latency, throughput'),
       
    49     cfg.StrOpt('zfssa_initiator_group', default='',
       
    50                help='iSCSI initiator group'),
       
    51     cfg.StrOpt('zfssa_initiator', default='',
       
    52                help='iSCSI initiator IQNs (comma separated)'),
       
    53     cfg.StrOpt('zfssa_initiator_user', default='',
       
    54                help='iSCSI initiator CHAP user'),
       
    55     cfg.StrOpt('zfssa_initiator_password', default='',
       
    56                help='iSCSI initiator CHAP password'),
       
    57     cfg.StrOpt('zfssa_target_group', default='tgt-grp',
       
    58                help='iSCSI target group name'),
       
    59     cfg.StrOpt('zfssa_target_user', default='',
       
    60                help='iSCSI target CHAP user'),
       
    61     cfg.StrOpt('zfssa_target_password', default='',
       
    62                help='iSCSI target CHAP password'),
       
    63     cfg.StrOpt('zfssa_target_portal', required=True,
       
    64                help='iSCSI target portal (Data-IP:Port, w.x.y.z:3260)'),
       
    65     cfg.StrOpt('zfssa_target_interfaces', required=True,
       
    66                help='Network interfaces of iSCSI targets (comma separated)')
       
    67 ]
       
    68 
       
    69 CONF.register_opts(ZFSSA_OPTS)
       
    70 
       
    71 SIZE_GB = 1073741824
       
    72 
       
    73 
       
    74 #pylint: disable=R0904
       
    75 class ZFSSAISCSIDriver(driver.ISCSIDriver):
       
    76     """ZFSSA Cinder volume driver"""
       
    77 
       
    78     VERSION = '1.0.0'
       
    79     protocol = 'iSCSI'
       
    80 
       
    81     def __init__(self, *args, **kwargs):
       
    82         super(ZFSSAISCSIDriver, self).__init__(*args, **kwargs)
       
    83         self.configuration.append_config_values(ZFSSA_OPTS)
       
    84         self.zfssa = None
       
    85         self._stats = None
       
    86 
       
    87     def _get_target_alias(self):
       
    88         """return target alias"""
       
    89         return self.configuration.zfssa_target_group
       
    90 
       
    91     def do_setup(self, context):
       
    92         """Setup - create project, initiators, initiatorgroup, target,
       
    93                    targetgroup
       
    94         """
       
    95         self.configuration._check_required_opts()
       
    96         lcfg = self.configuration
       
    97 
       
    98         LOG.info('Connecting to host: %s' % lcfg.zfssa_host)
       
    99         self.zfssa = zfssarest.ZFSSAApi(lcfg.zfssa_host)
       
   100         auth_str = base64.encodestring('%s:%s' %
       
   101                                        (lcfg.zfssa_auth_user,
       
   102                                         lcfg.zfssa_auth_password))[:-1]
       
   103         self.zfssa.login(auth_str)
       
   104         self.zfssa.create_project(lcfg.zfssa_pool, lcfg.zfssa_project,
       
   105                                   compression=lcfg.zfssa_lun_compression,
       
   106                                   logbias=lcfg.zfssa_lun_logbias)
       
   107 
       
   108         if (lcfg.zfssa_initiator != '' and
       
   109             (lcfg.zfssa_initiator_group == '' or
       
   110              lcfg.zfssa_initiator_group == 'default')):
       
   111             LOG.warning('zfssa_initiator= %s wont be used on \
       
   112                         zfssa_initiator_group= %s' %
       
   113                         (lcfg.zfssa_initiator,
       
   114                          lcfg.zfssa_initiator_group))
       
   115 
       
   116         # Setup initiator and initiator group
       
   117         if lcfg.zfssa_initiator != '' and \
       
   118            lcfg.zfssa_initiator_group != '' and \
       
   119            lcfg.zfssa_initiator_group != 'default':
       
   120             for initiator in lcfg.zfssa_initiator.split(','):
       
   121                 self.zfssa.create_initiator(initiator,
       
   122                                             lcfg.zfssa_initiator_group + '-' +
       
   123                                             initiator,
       
   124                                             chapuser=
       
   125                                             lcfg.zfssa_initiator_user,
       
   126                                             chapsecret=
       
   127                                             lcfg.zfssa_initiator_password)
       
   128                 self.zfssa.add_to_initiatorgroup(initiator,
       
   129                                                  lcfg.zfssa_initiator_group)
       
   130         # Parse interfaces
       
   131         interfaces = []
       
   132         for interface in lcfg.zfssa_target_interfaces.split(','):
       
   133             if interface == '':
       
   134                 continue
       
   135             interfaces.append(interface)
       
   136 
       
   137         # Setup target and target group
       
   138         iqn = self.zfssa.create_target(
       
   139             self._get_target_alias(),
       
   140             interfaces,
       
   141             tchapuser=lcfg.zfssa_target_user,
       
   142             tchapsecret=lcfg.zfssa_target_password)
       
   143 
       
   144         self.zfssa.add_to_targetgroup(iqn, lcfg.zfssa_target_group)
       
   145 
       
   146     def check_for_setup_error(self):
       
   147         """Check that driver can login and pool, project, initiators,
       
   148            initiatorgroup, target, targetgroup exist
       
   149         """
       
   150         lcfg = self.configuration
       
   151 
       
   152         self.zfssa.verify_pool(lcfg.zfssa_pool)
       
   153         self.zfssa.verify_project(lcfg.zfssa_pool, lcfg.zfssa_project)
       
   154 
       
   155         if lcfg.zfssa_initiator != '' and \
       
   156            lcfg.zfssa_initiator_group != '' and \
       
   157            lcfg.zfssa_initiator_group != 'default':
       
   158             for initiator in lcfg.zfssa_initiator.split(','):
       
   159                 self.zfssa.verify_initiator(initiator)
       
   160 
       
   161             self.zfssa.verify_target(self._get_target_alias())
       
   162 
       
   163     def _get_provider_info(self, volume):
       
   164         """return provider information"""
       
   165         lcfg = self.configuration
       
   166         lun = self.zfssa.get_lun(lcfg.zfssa_pool,
       
   167                                  lcfg.zfssa_project, volume['name'])
       
   168         iqn = self.zfssa.get_target(self._get_target_alias())
       
   169         loc = "%s %s %s" % (lcfg.zfssa_target_portal, iqn, lun['number'])
       
   170         LOG.debug('_export_volume: provider_location: %s' % loc)
       
   171         provider = {'provider_location': loc}
       
   172         if lcfg.zfssa_target_user != '' and lcfg.zfssa_target_password != '':
       
   173             provider['provider_auth'] = 'CHAP %s %s' % \
       
   174                                         (lcfg.zfssa_target_user,
       
   175                                          lcfg.zfssa_target_password)
       
   176         return provider
       
   177 
       
   178     def create_volume(self, volume):
       
   179         """Create a volume on ZFSSA"""
       
   180         LOG.debug('zfssa.create_volume: volume=' + volume['name'])
       
   181         lcfg = self.configuration
       
   182         volsize = str(volume['size']) + 'g'
       
   183         self.zfssa.create_lun(lcfg.zfssa_pool,
       
   184                               lcfg.zfssa_project,
       
   185                               volume['name'],
       
   186                               volsize,
       
   187                               targetgroup=lcfg.zfssa_target_group,
       
   188                               volblocksize=lcfg.zfssa_lun_volblocksize,
       
   189                               sparse=lcfg.zfssa_lun_sparse,
       
   190                               compression=lcfg.zfssa_lun_compression,
       
   191                               logbias=lcfg.zfssa_lun_logbias)
       
   192 
       
   193         return self._get_provider_info(volume)
       
   194 
       
   195     def delete_volume(self, volume):
       
   196         """Deletes a volume with the given volume['name']."""
       
   197         LOG.debug('zfssa.delete_volume: name=' + volume['name'])
       
   198         lcfg = self.configuration
       
   199         lun2del = self.zfssa.get_lun(lcfg.zfssa_pool,
       
   200                                      lcfg.zfssa_project,
       
   201                                      volume['name'])
       
   202         """Delete clone's temp snapshot. see create_cloned_volume()"""
       
   203         """clone is deleted as part of the snapshot delete."""
       
   204         tmpsnap = 'tmp-snapshot-%s' % volume['id']
       
   205         if 'origin' in lun2del and lun2del['origin']['snapshot'] == tmpsnap:
       
   206             self.zfssa.delete_snapshot(lcfg.zfssa_pool,
       
   207                                        lcfg.zfssa_project,
       
   208                                        lun2del['origin']['share'],
       
   209                                        lun2del['origin']['snapshot'])
       
   210             return
       
   211 
       
   212         self.zfssa.delete_lun(pool=lcfg.zfssa_pool,
       
   213                               project=lcfg.zfssa_project,
       
   214                               lun=volume['name'])
       
   215 
       
   216     def create_snapshot(self, snapshot):
       
   217         """Creates a snapshot with the given snapshot['name'] of the
       
   218            snapshot['volume_name']
       
   219         """
       
   220         LOG.debug('zfssa.create_snapshot: snapshot=' + snapshot['name'])
       
   221         lcfg = self.configuration
       
   222         self.zfssa.create_snapshot(lcfg.zfssa_pool,
       
   223                                    lcfg.zfssa_project,
       
   224                                    snapshot['volume_name'],
       
   225                                    snapshot['name'])
       
   226 
       
   227     def delete_snapshot(self, snapshot):
       
   228         """Deletes a snapshot."""
       
   229         LOG.debug('zfssa.delete_snapshot: snapshot=' + snapshot['name'])
       
   230         lcfg = self.configuration
       
   231         has_clones = self.zfssa.has_clones(lcfg.zfssa_pool,
       
   232                                            lcfg.zfssa_project,
       
   233                                            snapshot['volume_name'],
       
   234                                            snapshot['name'])
       
   235         if has_clones:
       
   236             LOG.error('snapshot %s: has clones' % snapshot['name'])
       
   237             raise exception.SnapshotIsBusy(snapshot_name=snapshot['name'])
       
   238 
       
   239         self.zfssa.delete_snapshot(lcfg.zfssa_pool,
       
   240                                    lcfg.zfssa_project,
       
   241                                    snapshot['volume_name'],
       
   242                                    snapshot['name'])
       
   243 
       
   244     def create_volume_from_snapshot(self, volume, snapshot):
       
   245         """Creates a volume from a snapshot - clone a snapshot"""
       
   246         LOG.debug('zfssa.create_volume_from_snapshot: volume=' +
       
   247                   volume['name'])
       
   248         LOG.debug('zfssa.create_volume_from_snapshot: snapshot=' +
       
   249                   snapshot['name'])
       
   250         if not self._verify_clone_size(snapshot, volume['size'] * SIZE_GB):
       
   251             exception_msg = (_('Error verifying clone size on '
       
   252                                'Volume clone: %(clone)s '
       
   253                                'Size: %(size)d on'
       
   254                                'Snapshot: %(snapshot)s')
       
   255                              % {'clone': volume['name'],
       
   256                                 'size': volume['size'],
       
   257                                 'snapshot': snapshot['name']})
       
   258             LOG.error(exception_msg)
       
   259             raise exception.InvalidInput(reason=exception_msg)
       
   260 
       
   261         lcfg = self.configuration
       
   262         self.zfssa.clone_snapshot(lcfg.zfssa_pool,
       
   263                                   lcfg.zfssa_project,
       
   264                                   snapshot['volume_name'],
       
   265                                   snapshot['name'],
       
   266                                   volume['name'])
       
   267 
       
   268     def _update_volume_status(self):
       
   269         """Retrieve status info from volume group."""
       
   270         LOG.debug("Updating volume status")
       
   271         self._stats = None
       
   272         data = {}
       
   273         data["volume_backend_name"] = self.__class__.__name__
       
   274         data["vendor_name"] = 'Oracle'
       
   275         data["driver_version"] = self.VERSION
       
   276         data["storage_protocol"] = self.protocol
       
   277 
       
   278         lcfg = self.configuration
       
   279         (avail, used) = self.zfssa.get_pool_stats(lcfg.zfssa_pool)
       
   280         if avail is None or used is None:
       
   281             return
       
   282         total = int(avail) + int(used)
       
   283 
       
   284         if lcfg.zfssa_lun_sparse:
       
   285             data['total_capacity_gb'] = 'infinite'
       
   286         else:
       
   287             data['total_capacity_gb'] = total / SIZE_GB
       
   288         data['free_capacity_gb'] = int(avail) / SIZE_GB
       
   289         data['reserved_percentage'] = 0
       
   290         data['QoS_support'] = False
       
   291         self._stats = data
       
   292 
       
   293     def get_volume_stats(self, refresh=False):
       
   294         """Get volume status.
       
   295            If 'refresh' is True, run update the stats first.
       
   296         """
       
   297         if refresh:
       
   298             self._update_volume_status()
       
   299         return self._stats
       
   300 
       
   301     def _export_volume(self, volume):
       
   302         """Export the volume - set the initiatorgroup property."""
       
   303         LOG.debug('_export_volume: volume name: %s' % volume['name'])
       
   304         lcfg = self.configuration
       
   305 
       
   306         self.zfssa.set_lun_initiatorgroup(lcfg.zfssa_pool,
       
   307                                           lcfg.zfssa_project,
       
   308                                           volume['name'],
       
   309                                           lcfg.zfssa_initiator_group)
       
   310         return self._get_provider_info(volume)
       
   311 
       
   312     def create_export(self, context, volume):
       
   313         """Driver entry point to get the  export info for a new volume."""
       
   314         LOG.debug('create_export: volume name: %s' % volume['name'])
       
   315         return self._export_volume(volume)
       
   316 
       
   317     def remove_export(self, context, volume):
       
   318         """Driver entry point to remove an export for a volume."""
       
   319         LOG.debug('remove_export: volume name: %s' % volume['name'])
       
   320         lcfg = self.configuration
       
   321         self.zfssa.set_lun_initiatorgroup(lcfg.zfssa_pool,
       
   322                                           lcfg.zfssa_project,
       
   323                                           volume['name'],
       
   324                                           '')
       
   325 
       
   326     def ensure_export(self, context, volume):
       
   327         """Driver entry point to get the export info for an existing volume."""
       
   328         LOG.debug('ensure_export: volume name: %s' % volume['name'])
       
   329         return self._export_volume(volume)
       
   330 
       
   331     def copy_image_to_volume(self, context, volume, image_service, image_id):
       
   332         self.ensure_export(context, volume)
       
   333         super(ZFSSAISCSIDriver, self).copy_image_to_volume(
       
   334             context, volume, image_service, image_id)
       
   335 
       
   336     def extend_volume(self, volume, new_size):
       
   337         """Driver entry point to extent volume size."""
       
   338         LOG.debug('extend_volume: volume name: %s' % volume['name'])
       
   339         lcfg = self.configuration
       
   340         self.zfssa.set_lun_size(lcfg.zfssa_pool,
       
   341                                 lcfg.zfssa_project,
       
   342                                 volume['name'],
       
   343                                 new_size * SIZE_GB)
       
   344 
       
   345     def create_cloned_volume(self, volume, src_vref):
       
   346         """Create a clone of the specified volume."""
       
   347         zfssa_snapshot = {'volume_name': src_vref['name'],
       
   348                           'name': 'tmp-snapshot-%s' % volume['id']}
       
   349         self.create_snapshot(zfssa_snapshot)
       
   350         try:
       
   351             self.create_volume_from_snapshot(volume, zfssa_snapshot)
       
   352         except exception.VolumeBackendAPIException:
       
   353             LOG.error("Clone Volume '%s' failed from source volume '%s'"
       
   354                       % (volume['name'], src_vref['name']))
       
   355             # Cleanup snapshot
       
   356             self.delete_snapshot(zfssa_snapshot)
       
   357 
       
   358     def local_path(self, volume):
       
   359         """Not implemented"""
       
   360         pass
       
   361 
       
   362     def backup_volume(self, context, backup, backup_service):
       
   363         """Not implemented"""
       
   364         pass
       
   365 
       
   366     def restore_backup(self, context, backup, volume, backup_service):
       
   367         """Not implemented"""
       
   368         pass
       
   369 
       
   370     def _verify_clone_size(self, snapshot, size):
       
   371         """Check whether the clone size is the same as the parent volume"""
       
   372         lcfg = self.configuration
       
   373         lun = self.zfssa.get_lun(lcfg.zfssa_pool,
       
   374                                  lcfg.zfssa_project,
       
   375                                  snapshot['volume_name'])
       
   376         return (lun['size'] == size)