components/openstack/cinder/files/zfssa/zfssaiscsi.py
branchs11u3-sru
changeset 6035 c9748fcc32de
equal deleted inserted replaced
6016:a477397bba8b 6035:c9748fcc32de
       
     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 ast
       
    18 import base64
       
    19 
       
    20 from oslo_config import cfg
       
    21 from oslo_log import log
       
    22 from oslo_utils import units
       
    23 
       
    24 from cinder import exception
       
    25 from cinder.i18n import _, _LE, _LW
       
    26 from cinder.volume import driver
       
    27 from cinder.volume.drivers.san import san
       
    28 from cinder.volume.drivers.zfssa import zfssarest
       
    29 from cinder.volume import volume_types
       
    30 
       
    31 CONF = cfg.CONF
       
    32 LOG = log.getLogger(__name__)
       
    33 
       
    34 ZFSSA_OPTS = [
       
    35     cfg.StrOpt('zfssa_pool',
       
    36                help='Storage pool name.'),
       
    37     cfg.StrOpt('zfssa_project',
       
    38                help='Project name.'),
       
    39     cfg.StrOpt('zfssa_lun_volblocksize', default='8k',
       
    40                choices=['512', '1k', '2k', '4k', '8k', '16k', '32k', '64k',
       
    41                         '128k'],
       
    42                help='Block size.'),
       
    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='off',
       
    46                choices=['off', 'lzjb', 'gzip-2', 'gzip', 'gzip-9'],
       
    47                help='Data compression.'),
       
    48     cfg.StrOpt('zfssa_lun_logbias', default='latency',
       
    49                choices=['latency', 'throughput'],
       
    50                help='Synchronous write bias.'),
       
    51     cfg.StrOpt('zfssa_initiator_group', default='',
       
    52                help='iSCSI initiator group.'),
       
    53     cfg.StrOpt('zfssa_initiator', default='',
       
    54                help='iSCSI initiator IQNs. (comma separated)'),
       
    55     cfg.StrOpt('zfssa_initiator_user', default='',
       
    56                help='iSCSI initiator CHAP user.'),
       
    57     cfg.StrOpt('zfssa_initiator_password', default='',
       
    58                help='iSCSI initiator CHAP password.', secret=True),
       
    59     cfg.StrOpt('zfssa_initiator_config', default='',
       
    60                help='iSCSI initiators configuration.'),
       
    61     cfg.StrOpt('zfssa_target_group', default='tgt-grp',
       
    62                help='iSCSI target group name.'),
       
    63     cfg.StrOpt('zfssa_target_user', default='',
       
    64                help='iSCSI target CHAP user.'),
       
    65     cfg.StrOpt('zfssa_target_password', default='',
       
    66                help='iSCSI target CHAP password.', secret=True),
       
    67     cfg.StrOpt('zfssa_target_portal',
       
    68                help='iSCSI target portal (Data-IP:Port, w.x.y.z:3260).'),
       
    69     cfg.StrOpt('zfssa_target_interfaces',
       
    70                help='Network interfaces of iSCSI targets. (comma separated)'),
       
    71     cfg.IntOpt('zfssa_rest_timeout',
       
    72                help='REST connection timeout. (seconds)')
       
    73 
       
    74 ]
       
    75 
       
    76 CONF.register_opts(ZFSSA_OPTS)
       
    77 
       
    78 ZFSSA_LUN_SPECS = {'zfssa:volblocksize',
       
    79                    'zfssa:sparse',
       
    80                    'zfssa:compression',
       
    81                    'zfssa:logbias'}
       
    82 
       
    83 
       
    84 def factory_zfssa():
       
    85     return zfssarest.ZFSSAApi()
       
    86 
       
    87 
       
    88 class ZFSSAISCSIDriver(driver.ISCSIDriver):
       
    89     """ZFSSA Cinder volume driver"""
       
    90 
       
    91     VERSION = '1.0.0'
       
    92     protocol = 'iSCSI'
       
    93 
       
    94     def __init__(self, *args, **kwargs):
       
    95         super(ZFSSAISCSIDriver, self).__init__(*args, **kwargs)
       
    96         self.configuration.append_config_values(ZFSSA_OPTS)
       
    97         self.configuration.append_config_values(san.san_opts)
       
    98         self.zfssa = None
       
    99         self._stats = None
       
   100 
       
   101     def _get_target_alias(self):
       
   102         """return target alias"""
       
   103         return self.configuration.zfssa_target_group
       
   104 
       
   105     def do_setup(self, context):
       
   106         """Setup - create multiple elements.
       
   107 
       
   108         Project, initiators, initiatorgroup, target and targetgroup.
       
   109         """
       
   110         lcfg = self.configuration
       
   111         msg = (_('Connecting to host: %s.') % lcfg.san_ip)
       
   112         LOG.info(msg)
       
   113         self.zfssa = factory_zfssa()
       
   114         self.zfssa.set_host(lcfg.san_ip, timeout=lcfg.zfssa_rest_timeout)
       
   115         auth_str = base64.encodestring('%s:%s' %
       
   116                                        (lcfg.san_login,
       
   117                                         lcfg.san_password))[:-1]
       
   118         self.zfssa.login(auth_str)
       
   119         self.zfssa.create_project(lcfg.zfssa_pool, lcfg.zfssa_project,
       
   120                                   compression=lcfg.zfssa_lun_compression,
       
   121                                   logbias=lcfg.zfssa_lun_logbias)
       
   122 
       
   123         if (lcfg.zfssa_initiator_config != ''):
       
   124             initiator_config = ast.literal_eval(lcfg.zfssa_initiator_config)
       
   125             for initiator_group in initiator_config:
       
   126                 zfssa_initiator_group = initiator_group
       
   127                 for zfssa_initiator in initiator_config[zfssa_initiator_group]:
       
   128                     self.zfssa.create_initiator(zfssa_initiator['iqn'],
       
   129                                                 zfssa_initiator_group + '-' +
       
   130                                                 zfssa_initiator['iqn'],
       
   131                                                 chapuser=
       
   132                                                 zfssa_initiator['user'],
       
   133                                                 chapsecret=
       
   134                                                 zfssa_initiator['password'])
       
   135                     if (zfssa_initiator_group != 'default'):
       
   136                         self.zfssa.add_to_initiatorgroup(
       
   137                             zfssa_initiator['iqn'],
       
   138                             zfssa_initiator_group)
       
   139         else:
       
   140             LOG.warning(_LW('zfssa_initiator_config not found. '
       
   141                             'Using deprecated configuration options.'))
       
   142             if (lcfg.zfssa_initiator != '' and
       
   143                 (lcfg.zfssa_initiator_group == '' or
       
   144                  lcfg.zfssa_initiator_group == 'default')):
       
   145                 LOG.warning(_LW('zfssa_initiator: %(ini)s'
       
   146                                 ' wont be used on '
       
   147                                 'zfssa_initiator_group= %(inigrp)s.')
       
   148                             % {'ini': lcfg.zfssa_initiator,
       
   149                                'inigrp': lcfg.zfssa_initiator_group})
       
   150 
       
   151             # Setup initiator and initiator group
       
   152             if (lcfg.zfssa_initiator != '' and
       
   153                lcfg.zfssa_initiator_group != '' and
       
   154                lcfg.zfssa_initiator_group != 'default'):
       
   155                 for initiator in lcfg.zfssa_initiator.split(','):
       
   156                     self.zfssa.create_initiator(
       
   157                         initiator, lcfg.zfssa_initiator_group + '-' +
       
   158                         initiator, chapuser=lcfg.zfssa_initiator_user,
       
   159                         chapsecret=lcfg.zfssa_initiator_password)
       
   160                     self.zfssa.add_to_initiatorgroup(
       
   161                         initiator, lcfg.zfssa_initiator_group)
       
   162 
       
   163         # Parse interfaces
       
   164         interfaces = []
       
   165         for interface in lcfg.zfssa_target_interfaces.split(','):
       
   166             if interface == '':
       
   167                 continue
       
   168             interfaces.append(interface)
       
   169 
       
   170         # Setup target and target group
       
   171         iqn = self.zfssa.create_target(
       
   172             self._get_target_alias(),
       
   173             interfaces,
       
   174             tchapuser=lcfg.zfssa_target_user,
       
   175             tchapsecret=lcfg.zfssa_target_password)
       
   176 
       
   177         self.zfssa.add_to_targetgroup(iqn, lcfg.zfssa_target_group)
       
   178 
       
   179     def check_for_setup_error(self):
       
   180         """Check that driver can login.
       
   181 
       
   182         Check also pool, project, initiators, initiatorgroup, target and
       
   183         targetgroup.
       
   184         """
       
   185         lcfg = self.configuration
       
   186 
       
   187         self.zfssa.verify_pool(lcfg.zfssa_pool)
       
   188         self.zfssa.verify_project(lcfg.zfssa_pool, lcfg.zfssa_project)
       
   189 
       
   190         if (lcfg.zfssa_initiator_config != ''):
       
   191             initiator_config = ast.literal_eval(lcfg.zfssa_initiator_config)
       
   192             for initiator_group in initiator_config:
       
   193                 zfssa_initiator_group = initiator_group
       
   194                 for zfssa_initiator in initiator_config[zfssa_initiator_group]:
       
   195                     self.zfssa.verify_initiator(zfssa_initiator['iqn'])
       
   196         else:
       
   197             if (lcfg.zfssa_initiator != '' and
       
   198                lcfg.zfssa_initiator_group != '' and
       
   199                lcfg.zfssa_initiator_group != 'default'):
       
   200                 for initiator in lcfg.zfssa_initiator.split(','):
       
   201                     self.zfssa.verify_initiator(initiator)
       
   202 
       
   203             self.zfssa.verify_target(self._get_target_alias())
       
   204 
       
   205     def _get_provider_info(self, volume):
       
   206         """return provider information"""
       
   207         lcfg = self.configuration
       
   208         lun = self.zfssa.get_lun(lcfg.zfssa_pool,
       
   209                                  lcfg.zfssa_project, volume['name'])
       
   210         iqn = self.zfssa.get_target(self._get_target_alias())
       
   211         loc = "%s %s %s" % (lcfg.zfssa_target_portal, iqn, lun['number'])
       
   212         LOG.debug('_get_provider_info: provider_location: %s' % loc)
       
   213         provider = {'provider_location': loc}
       
   214         if lcfg.zfssa_target_user != '' and lcfg.zfssa_target_password != '':
       
   215             provider['provider_auth'] = ('CHAP %s %s' %
       
   216                                          lcfg.zfssa_target_user,
       
   217                                          lcfg.zfssa_target_password)
       
   218 
       
   219         return provider
       
   220 
       
   221     def create_volume(self, volume):
       
   222         """Create a volume on ZFSSA"""
       
   223         LOG.debug('zfssa.create_volume: volume=' + volume['name'])
       
   224         lcfg = self.configuration
       
   225         volsize = str(volume['size']) + 'g'
       
   226         specs = self._get_voltype_specs(volume)
       
   227         self.zfssa.create_lun(lcfg.zfssa_pool,
       
   228                               lcfg.zfssa_project,
       
   229                               volume['name'],
       
   230                               volsize,
       
   231                               lcfg.zfssa_target_group,
       
   232                               specs)
       
   233 
       
   234     def delete_volume(self, volume):
       
   235         """Deletes a volume with the given volume['name']."""
       
   236         LOG.debug('zfssa.delete_volume: name=' + volume['name'])
       
   237         lcfg = self.configuration
       
   238         lun2del = self.zfssa.get_lun(lcfg.zfssa_pool,
       
   239                                      lcfg.zfssa_project,
       
   240                                      volume['name'])
       
   241         # Delete clone temp snapshot. see create_cloned_volume()
       
   242         if 'origin' in lun2del and 'id' in volume:
       
   243             if lun2del['nodestroy']:
       
   244                 self.zfssa.set_lun_props(lcfg.zfssa_pool,
       
   245                                          lcfg.zfssa_project,
       
   246                                          volume['name'],
       
   247                                          nodestroy=False)
       
   248 
       
   249             tmpsnap = 'tmp-snapshot-%s' % volume['id']
       
   250             if lun2del['origin']['snapshot'] == tmpsnap:
       
   251                 self.zfssa.delete_snapshot(lcfg.zfssa_pool,
       
   252                                            lcfg.zfssa_project,
       
   253                                            lun2del['origin']['share'],
       
   254                                            lun2del['origin']['snapshot'])
       
   255                 return
       
   256 
       
   257         self.zfssa.delete_lun(pool=lcfg.zfssa_pool,
       
   258                               project=lcfg.zfssa_project,
       
   259                               lun=volume['name'])
       
   260 
       
   261     def create_snapshot(self, snapshot):
       
   262         """Creates a snapshot with the given snapshot['name'] of the
       
   263            snapshot['volume_name']
       
   264         """
       
   265         LOG.debug('zfssa.create_snapshot: snapshot=' + snapshot['name'])
       
   266         lcfg = self.configuration
       
   267         self.zfssa.create_snapshot(lcfg.zfssa_pool,
       
   268                                    lcfg.zfssa_project,
       
   269                                    snapshot['volume_name'],
       
   270                                    snapshot['name'])
       
   271 
       
   272     def delete_snapshot(self, snapshot):
       
   273         """Deletes a snapshot."""
       
   274         LOG.debug('zfssa.delete_snapshot: snapshot=' + snapshot['name'])
       
   275         lcfg = self.configuration
       
   276         has_clones = self.zfssa.has_clones(lcfg.zfssa_pool,
       
   277                                            lcfg.zfssa_project,
       
   278                                            snapshot['volume_name'],
       
   279                                            snapshot['name'])
       
   280         if has_clones:
       
   281             LOG.error(_LE('Snapshot %s: has clones') % snapshot['name'])
       
   282             raise exception.SnapshotIsBusy(snapshot_name=snapshot['name'])
       
   283 
       
   284         self.zfssa.delete_snapshot(lcfg.zfssa_pool,
       
   285                                    lcfg.zfssa_project,
       
   286                                    snapshot['volume_name'],
       
   287                                    snapshot['name'])
       
   288 
       
   289     def create_volume_from_snapshot(self, volume, snapshot):
       
   290         """Creates a volume from a snapshot - clone a snapshot"""
       
   291         LOG.debug('zfssa.create_volume_from_snapshot: volume=' +
       
   292                   volume['name'])
       
   293         LOG.debug('zfssa.create_volume_from_snapshot: snapshot=' +
       
   294                   snapshot['name'])
       
   295         if not self._verify_clone_size(snapshot, volume['size'] * units.Gi):
       
   296             exception_msg = (_('Error verifying clone size on '
       
   297                                'Volume clone: %(clone)s '
       
   298                                'Size: %(size)d on'
       
   299                                'Snapshot: %(snapshot)s')
       
   300                              % {'clone': volume['name'],
       
   301                                 'size': volume['size'],
       
   302                                 'snapshot': snapshot['name']})
       
   303             LOG.error(exception_msg)
       
   304             raise exception.InvalidInput(reason=exception_msg)
       
   305 
       
   306         lcfg = self.configuration
       
   307         self.zfssa.clone_snapshot(lcfg.zfssa_pool,
       
   308                                   lcfg.zfssa_project,
       
   309                                   snapshot['volume_name'],
       
   310                                   snapshot['name'],
       
   311                                   volume['name'])
       
   312 
       
   313     def _update_volume_status(self):
       
   314         """Retrieve status info from volume group."""
       
   315         LOG.debug("Updating volume status")
       
   316         self._stats = None
       
   317         data = {}
       
   318         backend_name = self.configuration.safe_get('volume_backend_name')
       
   319         data["volume_backend_name"] = backend_name or self.__class__.__name__
       
   320         data["vendor_name"] = 'Oracle'
       
   321         data["driver_version"] = self.VERSION
       
   322         data["storage_protocol"] = self.protocol
       
   323 
       
   324         lcfg = self.configuration
       
   325         (avail, total) = self.zfssa.get_pool_stats(lcfg.zfssa_pool)
       
   326         if avail is None or total is None:
       
   327             return
       
   328 
       
   329         data['total_capacity_gb'] = int(total) / units.Gi
       
   330         data['free_capacity_gb'] = int(avail) / units.Gi
       
   331         data['reserved_percentage'] = 0
       
   332         data['QoS_support'] = False
       
   333         self._stats = data
       
   334 
       
   335     def get_volume_stats(self, refresh=False):
       
   336         """Get volume status.
       
   337            If 'refresh' is True, run update the stats first.
       
   338         """
       
   339         if refresh:
       
   340             self._update_volume_status()
       
   341         return self._stats
       
   342 
       
   343     def create_export(self, context, volume):
       
   344         pass
       
   345 
       
   346     def remove_export(self, context, volume):
       
   347         pass
       
   348 
       
   349     def ensure_export(self, context, volume):
       
   350         pass
       
   351 
       
   352     def copy_image_to_volume(self, context, volume, image_service, image_id):
       
   353         self.ensure_export(context, volume)
       
   354         super(ZFSSAISCSIDriver, self).copy_image_to_volume(
       
   355             context, volume, image_service, image_id)
       
   356 
       
   357     def extend_volume(self, volume, new_size):
       
   358         """Driver entry point to extent volume size."""
       
   359         LOG.debug('extend_volume: volume name: %s' % volume['name'])
       
   360         lcfg = self.configuration
       
   361         self.zfssa.set_lun_props(lcfg.zfssa_pool,
       
   362                                  lcfg.zfssa_project,
       
   363                                  volume['name'],
       
   364                                  volsize=new_size * units.Gi)
       
   365 
       
   366     def create_cloned_volume(self, volume, src_vref):
       
   367         """Create a clone of the specified volume."""
       
   368         zfssa_snapshot = {'volume_name': src_vref['name'],
       
   369                           'name': 'tmp-snapshot-%s' % volume['id']}
       
   370         self.create_snapshot(zfssa_snapshot)
       
   371         try:
       
   372             self.create_volume_from_snapshot(volume, zfssa_snapshot)
       
   373         except exception.VolumeBackendAPIException:
       
   374             LOG.error(_LE('Clone Volume:'
       
   375                           '%(volume)s failed from source volume:'
       
   376                           '%(src_vref)s')
       
   377                       % {'volume': volume['name'],
       
   378                          'src_vref': src_vref['name']})
       
   379             # Cleanup snapshot
       
   380             self.delete_snapshot(zfssa_snapshot)
       
   381 
       
   382     def local_path(self, volume):
       
   383         """Not implemented"""
       
   384         pass
       
   385 
       
   386     def backup_volume(self, context, backup, backup_service):
       
   387         """Not implemented"""
       
   388         pass
       
   389 
       
   390     def restore_backup(self, context, backup, volume, backup_service):
       
   391         """Not implemented"""
       
   392         pass
       
   393 
       
   394     def _verify_clone_size(self, snapshot, size):
       
   395         """Check whether the clone size is the same as the parent volume"""
       
   396         lcfg = self.configuration
       
   397         lun = self.zfssa.get_lun(lcfg.zfssa_pool,
       
   398                                  lcfg.zfssa_project,
       
   399                                  snapshot['volume_name'])
       
   400         return lun['size'] == size
       
   401 
       
   402     def initialize_connection(self, volume, connector):
       
   403         lcfg = self.configuration
       
   404         init_groups = self.zfssa.get_initiator_initiatorgroup(
       
   405             connector['initiator'])
       
   406         for initiator_group in init_groups:
       
   407             self.zfssa.set_lun_initiatorgroup(lcfg.zfssa_pool,
       
   408                                               lcfg.zfssa_project,
       
   409                                               volume['name'],
       
   410                                               initiator_group)
       
   411         iscsi_properties = {}
       
   412         provider = self._get_provider_info(volume)
       
   413         (target_portal, iqn, lun) = provider['provider_location'].split()
       
   414         iscsi_properties['target_discovered'] = False
       
   415         iscsi_properties['target_portal'] = target_portal
       
   416         iscsi_properties['target_iqn'] = iqn
       
   417         iscsi_properties['target_lun'] = lun
       
   418         iscsi_properties['volume_id'] = volume['id']
       
   419 
       
   420         if 'provider_auth' in provider:
       
   421             (auth_method, auth_username, auth_password) = provider[
       
   422                 'provider_auth'].split()
       
   423             iscsi_properties['auth_method'] = auth_method
       
   424             iscsi_properties['auth_username'] = auth_username
       
   425             iscsi_properties['auth_password'] = auth_password
       
   426 
       
   427         return {
       
   428             'driver_volume_type': 'iscsi',
       
   429             'data': iscsi_properties
       
   430         }
       
   431 
       
   432     def terminate_connection(self, volume, connector, **kwargs):
       
   433         """Driver entry point to terminate a connection for a volume."""
       
   434         LOG.debug('terminate_connection: volume name: %s.' % volume['name'])
       
   435         lcfg = self.configuration
       
   436         self.zfssa.set_lun_initiatorgroup(lcfg.zfssa_pool,
       
   437                                           lcfg.zfssa_project,
       
   438                                           volume['name'],
       
   439                                           '')
       
   440 
       
   441     def _get_voltype_specs(self, volume):
       
   442         """Get specs suitable for volume creation."""
       
   443         vtype = volume.get('volume_type_id', None)
       
   444         extra_specs = None
       
   445         if vtype:
       
   446             extra_specs = volume_types.get_volume_type_extra_specs(vtype)
       
   447 
       
   448         return self._get_specs(extra_specs)
       
   449 
       
   450     def _get_specs(self, xspecs):
       
   451         """Return a dict with extra specs and/or config values."""
       
   452         result = {}
       
   453         for spc in ZFSSA_LUN_SPECS:
       
   454             val = None
       
   455             prop = spc.split(':')[1]
       
   456             cfg = 'zfssa_lun_' + prop
       
   457             if xspecs:
       
   458                 val = xspecs.pop(spc, None)
       
   459 
       
   460             if val is None:
       
   461                 val = self.configuration.safe_get(cfg)
       
   462 
       
   463             if val is not None and val != '':
       
   464                 result.update({prop: val})
       
   465 
       
   466         return result