components/openstack/cinder/files/solaris/zfs.py
changeset 5405 66fd59fecd68
parent 5178 39289ddbd44e
child 6070 87daa7413b2d
equal deleted inserted replaced
5404:55e409ba4e72 5405:66fd59fecd68
     1 # vim: tabstop=4 shiftwidth=4 softtabstop=4
     1 # vim: tabstop=4 shiftwidth=4 softtabstop=4
     2 # Copyright (c) 2012 OpenStack LLC.
     2 # Copyright (c) 2012 OpenStack LLC.
     3 # All Rights Reserved.
     3 # All Rights Reserved.
     4 #
     4 #
     5 # Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
     5 # Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
     6 #
     6 #
     7 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
     7 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
     8 #    not use this file except in compliance with the License. You may obtain
     8 #    not use this file except in compliance with the License. You may obtain
     9 #    a copy of the License at
     9 #    a copy of the License at
    10 #
    10 #
    20 """
    20 """
    21 
    21 
    22 import abc
    22 import abc
    23 import fcntl
    23 import fcntl
    24 import os
    24 import os
    25 import socket
       
    26 import subprocess
    25 import subprocess
    27 import time
    26 import time
    28 
    27 
    29 from oslo.config import cfg
    28 from oslo_concurrency import processutils
       
    29 from oslo_config import cfg
       
    30 from oslo_log import log as logging
    30 import paramiko
    31 import paramiko
    31 
    32 
    32 from cinder import exception
    33 from cinder import exception
    33 from cinder.i18n import _
    34 from cinder.i18n import _, _LE, _LI
    34 from cinder.image import image_utils
    35 from cinder.image import image_utils
    35 from cinder.openstack.common import log as logging
       
    36 from cinder.openstack.common import processutils
       
    37 from cinder.volume import driver
    36 from cinder.volume import driver
    38 from cinder.volume.drivers.san.san import SanDriver
    37 from cinder.volume.drivers.san.san import SanDriver
       
    38 
       
    39 from eventlet.green import socket
       
    40 from eventlet.green.OpenSSL import SSL
       
    41 
       
    42 import rad.client as radc
       
    43 import rad.connect as radcon
       
    44 import rad.bindings.com.oracle.solaris.rad.zfsmgr_1 as zfsmgr
       
    45 import rad.auth as rada
    39 
    46 
    40 from solaris_install.target.size import Size
    47 from solaris_install.target.size import Size
    41 
    48 
    42 FLAGS = cfg.CONF
    49 FLAGS = cfg.CONF
    43 LOG = logging.getLogger(__name__)
    50 LOG = logging.getLogger(__name__)
    51                help='iSCSI target group name.'), ]
    58                help='iSCSI target group name.'), ]
    52 
    59 
    53 FLAGS.register_opts(solaris_zfs_opts)
    60 FLAGS.register_opts(solaris_zfs_opts)
    54 
    61 
    55 
    62 
       
    63 def connect_tls(host, port=12302, locale=None, ca_certs=None):
       
    64     """Connect to a RAD instance over TLS.
       
    65 
       
    66     Arguments:
       
    67     host     string, target host
       
    68     port     int, target port (RAD_PORT_TLS = 12302)
       
    69     locale   string, locale
       
    70     ca_certs string, path to file containing CA certificates
       
    71 
       
    72     Returns:
       
    73     RadConnection: a connection to RAD
       
    74     """
       
    75     # We don't want SSL 2.0, SSL 3.0 nor TLS 1.0 in RAD
       
    76     context = SSL.Context(SSL.SSLv23_METHOD)
       
    77     context.set_options(SSL.OP_NO_SSLv2)
       
    78     context.set_options(SSL.OP_NO_SSLv3)
       
    79     context.set_options(SSL.OP_NO_TLSv1)
       
    80 
       
    81     if ca_certs is not None:
       
    82         context.set_verify(SSL.VERIFY_PEER, _tls_verify_cb)
       
    83         context.load_verify_locations(ca_certs)
       
    84 
       
    85     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
    86     sock = SSL.Connection(context, sock)
       
    87     sock.connect((host, port))
       
    88     sock.do_handshake()
       
    89 
       
    90     return radcon.RadConnection(sock, locale=locale)
       
    91 
       
    92 
    56 class ZFSVolumeDriver(SanDriver):
    93 class ZFSVolumeDriver(SanDriver):
    57     """Local ZFS volume operations."""
    94     """OpenStack Cinder ZFS volume driver for generic ZFS volumes.
       
    95 
       
    96     Version history:
       
    97         1.0.0 - Initial driver with basic functionalities in Havana
       
    98         1.1.0 - Support SAN for the remote storage nodes access in Juno
       
    99         1.1.1 - Add support for the volume backup
       
   100         1.1.2 - Add support for the volume migration
       
   101         1.2.0 - Add support for the volume management in Kilo
       
   102         1.2.1 - Enable the connect_tls by importing eventlet.green.socket
       
   103         1.2.2 - Introduce the ZFS RAD for volume migration enhancement
       
   104         1.2.3 - Replace volume-specific targets with one shared target in
       
   105                 the ZFSISCSIDriver
       
   106 
       
   107     """
       
   108 
       
   109     version = "1.2.3"
    58     protocol = 'local'
   110     protocol = 'local'
    59 
   111 
    60     def __init__(self, *args, **kwargs):
   112     def __init__(self, *args, **kwargs):
    61         super(ZFSVolumeDriver, self).__init__(execute=self.solaris_execute,
   113         super(ZFSVolumeDriver, self).__init__(execute=self.solaris_execute,
    62                                               *args, **kwargs)
   114                                               *args, **kwargs)
   200     def attach_volume(self, context, volume, instance_uuid, host_name,
   252     def attach_volume(self, context, volume, instance_uuid, host_name,
   201                       mountpoint):
   253                       mountpoint):
   202         """Callback for volume attached to instance or host."""
   254         """Callback for volume attached to instance or host."""
   203         pass
   255         pass
   204 
   256 
   205     def detach_volume(self, context, volume):
   257     def detach_volume(self, context, volume, attachment):
   206         """ Callback for volume detached."""
   258         """ Callback for volume detached."""
   207         pass
   259         pass
   208 
   260 
   209     def get_volume_stats(self, refresh=False):
   261     def get_volume_stats(self, refresh=False):
   210         """Get volume status."""
   262         """Get volume status."""
   211 
   263 
   212         if refresh:
   264         if refresh:
   213             self._update_volume_stats()
   265             self._update_volume_stats()
   214 
   266 
   215         return self._stats
   267         return self._stats
   216 
       
   217     def copy_image_to_volume(self, context, volume, image_service, image_id):
       
   218         """Fetch the image from image_service and write it to the volume."""
       
   219         raise NotImplementedError()
       
   220 
       
   221     def copy_volume_to_image(self, context, volume, image_service, image_meta):
       
   222         """Copy the volume to the specified image."""
       
   223         raise NotImplementedError()
       
   224 
   268 
   225     def _get_zfs_property(self, prop, dataset):
   269     def _get_zfs_property(self, prop, dataset):
   226         """Get the value of property for the dataset."""
   270         """Get the value of property for the dataset."""
   227         try:
   271         try:
   228             (out, _err) = self._execute('/usr/sbin/zfs', 'get', '-H', '-o',
   272             (out, _err) = self._execute('/usr/sbin/zfs', 'get', '-H', '-o',
   229                                         'value', prop, dataset)
   273                                         'value', prop, dataset)
   230             return out.rstrip()
   274             return out.rstrip()
   231         except processutils.ProcessExecutionError:
   275         except processutils.ProcessExecutionError:
   232             LOG.info(_("Failed to get the property '%s' of the dataset '%s'") %
   276             LOG.info(_LI("Failed to get the property '%s' of the dataset '%s'")
   233                      (prop, dataset))
   277                      % (prop, dataset))
   234             return None
   278             return None
   235 
   279 
   236     def _get_zfs_snap_name(self, snapshot):
   280     def _get_zfs_snap_name(self, snapshot):
   237         """Get the snapshot path."""
   281         """Get the snapshot path."""
   238         return "%s/%s@%s" % (self.configuration.zfs_volume_base,
   282         return "%s/%s@%s" % (self.configuration.zfs_volume_base,
   271 
   315 
   272         try:
   316         try:
   273             p1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE,
   317             p1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE,
   274                                   stderr=subprocess.PIPE)
   318                                   stderr=subprocess.PIPE)
   275         except:
   319         except:
   276             LOG.error(_("_piped_execute '%s' failed.") % (cmd1))
   320             LOG.error(_LE("_piped_execute '%s' failed.") % (cmd1))
   277             raise
   321             raise
   278 
   322 
   279         # Set the pipe to be blocking because evenlet.green.subprocess uses
   323         # Set the pipe to be blocking because evenlet.green.subprocess uses
   280         # the non-blocking pipe.
   324         # the non-blocking pipe.
   281         flags = fcntl.fcntl(p1.stdout, fcntl.F_GETFL) & (~os.O_NONBLOCK)
   325         flags = fcntl.fcntl(p1.stdout, fcntl.F_GETFL) & (~os.O_NONBLOCK)
   306 
   350 
   307         cmd1 = ['/usr/sbin/zfs', 'send', src_snapshot_name]
   351         cmd1 = ['/usr/sbin/zfs', 'send', src_snapshot_name]
   308         cmd2 = ['/usr/sbin/zfs', 'receive', dst]
   352         cmd2 = ['/usr/sbin/zfs', 'receive', dst]
   309         # Due to pipe injection protection in the ssh utils method,
   353         # Due to pipe injection protection in the ssh utils method,
   310         # cinder.utils.check_ssh_injection(), the piped commands must be passed
   354         # cinder.utils.check_ssh_injection(), the piped commands must be passed
   311         # through via paramiko.  These commands take no user defined input
   355         # through via paramiko. These commands take no user defined input
   312         # other than the names of the zfs datasets which are already protected
   356         # other than the names of the zfs datasets which are already protected
   313         # against the special characters of concern.
   357         # against the special characters of concern.
   314         if not self.run_local:
   358         if not self.run_local:
   315             ip = self.configuration.san_ip
   359             ip = self.configuration.san_ip
   316             username = self.configuration.san_login
   360             username = self.configuration.san_login
   323         self.delete_snapshot(src_snapshot)
   367         self.delete_snapshot(src_snapshot)
   324         dst_snapshot_name = "%s@tmp-snapshot-%s" % (dst, src['id'])
   368         dst_snapshot_name = "%s@tmp-snapshot-%s" % (dst, src['id'])
   325         cmd = ['/usr/sbin/zfs', 'destroy', dst_snapshot_name]
   369         cmd = ['/usr/sbin/zfs', 'destroy', dst_snapshot_name]
   326         self._execute(*cmd)
   370         self._execute(*cmd)
   327 
   371 
       
   372     def _get_rc_connect(self, san_info=None):
       
   373         """Connect the RAD server."""
       
   374         if san_info is not None:
       
   375             san_ip = san_info.split(';')[0]
       
   376             san_login = san_info.split(';')[1]
       
   377             san_password = san_info.split(';')[2]
       
   378         else:
       
   379             san_ip = self.configuration.san_ip
       
   380             san_login = self.configuration.san_login
       
   381             san_password = self.configuration.san_password
       
   382 
       
   383         rc = connect_tls(san_ip)
       
   384         auth = rada.RadAuth(rc)
       
   385         auth.pam_login(san_login, san_password)
       
   386 
       
   387         return rc
       
   388 
       
   389     def _rad_zfs_send_recv(self, src, dst, dst_san_info=None):
       
   390         """Replicate the ZFS dataset stream."""
       
   391         src_snapshot = {'volume_name': src['name'],
       
   392                         'name': 'tmp-send-snapshot-%s' % src['id']}
       
   393         src_snapshot_name = self._get_zfs_snap_name(src_snapshot)
       
   394         prop_type = self._get_zfs_property('type', src_snapshot_name)
       
   395         # Delete the temporary snapshot if it already exists
       
   396         if prop_type == 'snapshot':
       
   397             self.delete_snapshot(src_snapshot)
       
   398         # Create the temporary snapshot of src volume
       
   399         self.create_snapshot(src_snapshot)
       
   400 
       
   401         src_rc = self._get_rc_connect()
       
   402         dst_rc = self._get_rc_connect(dst_san_info)
       
   403 
       
   404         src_pat = self._get_zfs_volume_name(src['name'])
       
   405         src_vol_obj = src_rc.get_object(zfsmgr.ZfsDataset(),
       
   406                                         radc.ADRGlobPattern({"name": src_pat}))
       
   407         dst_pat = dst.rsplit('/', 1)[0]
       
   408         dst_vol_obj = dst_rc.get_object(zfsmgr.ZfsDataset(),
       
   409                                         radc.ADRGlobPattern({"name": dst_pat}))
       
   410 
       
   411         send_sock_info = src_vol_obj.get_send_socket(
       
   412             name=src_snapshot_name, socket_type=zfsmgr.SocketType.AF_INET)
       
   413         send_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
   414         send_sock.connect((self.hostname, int(send_sock_info.socket)))
       
   415 
       
   416         dst_san_ip = dst_san_info.split(';')[0]
       
   417         remote_host, alias, addresslist = socket.gethostbyaddr(dst_san_ip)
       
   418 
       
   419         recv_sock_info = dst_vol_obj.get_receive_socket(
       
   420             name=dst, socket_type=zfsmgr.SocketType.AF_INET,
       
   421             name_options=zfsmgr.ZfsRecvNameOptions.use_provided_name)
       
   422         recv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
   423         recv_sock.connect((remote_host, int(recv_sock_info.socket)))
       
   424 
       
   425         # Set 4mb buffer size
       
   426         buf_size = 4194304
       
   427         while True:
       
   428             # Read the data from the send stream
       
   429             buf = send_sock.recv(buf_size)
       
   430             if not buf:
       
   431                 break
       
   432             # Write the data to the receive steam
       
   433             recv_sock.send(buf)
       
   434 
       
   435         recv_sock.close()
       
   436         send_sock.close()
       
   437         time.sleep(1)
       
   438 
       
   439         # Delete the temporary dst snapshot
       
   440         pat = radc.ADRGlobPattern({"name": dst})
       
   441         dst_zvol_obj = dst_rc.get_object(zfsmgr.ZfsDataset(), pat)
       
   442         snapshot_list = dst_zvol_obj.get_snapshots()
       
   443         for snap in snapshot_list:
       
   444             if 'tmp-send-snapshot'in snap:
       
   445                 dst_zvol_obj.destroy_snapshot(snap)
       
   446                 break
       
   447 
       
   448         # Delete the temporary src snapshot
       
   449         self.delete_snapshot(src_snapshot)
       
   450         LOG.debug(("Transfered src stream'%s' to dst'%s' on the host'%s'") %
       
   451                   (src_snapshot_name, dst, self.hostname))
       
   452 
       
   453         src_rc.close()
       
   454         dst_rc.close()
       
   455 
   328     def _get_zvol_path(self, volume):
   456     def _get_zvol_path(self, volume):
   329         """Get the ZFS volume path."""
   457         """Get the ZFS volume path."""
   330         return "/dev/zvol/rdsk/%s" % self._get_zfs_volume_name(volume['name'])
   458         return "/dev/zvol/rdsk/%s" % self._get_zfs_volume_name(volume['name'])
   331 
   459 
   332     def _update_volume_stats(self):
   460     def _update_volume_stats(self):
   335         LOG.debug(_("Updating volume status"))
   463         LOG.debug(_("Updating volume status"))
   336         stats = {}
   464         stats = {}
   337         backend_name = self.configuration.safe_get('volume_backend_name')
   465         backend_name = self.configuration.safe_get('volume_backend_name')
   338         stats["volume_backend_name"] = backend_name or self.__class__.__name__
   466         stats["volume_backend_name"] = backend_name or self.__class__.__name__
   339         stats["storage_protocol"] = self.protocol
   467         stats["storage_protocol"] = self.protocol
   340         stats["driver_version"] = '1.0'
   468         stats["driver_version"] = self.version
   341         stats["vendor_name"] = 'Oracle'
   469         stats["vendor_name"] = 'Oracle'
   342         stats['QoS_support'] = False
   470         stats['QoS_support'] = False
   343 
   471 
   344         dataset = self.configuration.zfs_volume_base
   472         dataset = self.configuration.zfs_volume_base
   345         used_size = self._get_zfs_property('used', dataset)
   473         used_size = self._get_zfs_property('used', dataset)
   346         avail_size = self._get_zfs_property('avail', dataset)
   474         avail_size = self._get_zfs_property('avail', dataset)
   347         stats['total_capacity_gb'] = \
   475         stats['total_capacity_gb'] = \
   348             (Size(used_size) + Size(avail_size)).get(Size.gb_units)
   476             (Size(used_size) + Size(avail_size)).get(Size.gb_units)
   349         stats['free_capacity_gb'] = Size(avail_size).get(Size.gb_units)
   477         stats['free_capacity_gb'] = Size(avail_size).get(Size.gb_units)
   350         stats['reserved_percentage'] = self.configuration.reserved_percentage
   478         stats['reserved_percentage'] = self.configuration.reserved_percentage
       
   479 
   351         stats['location_info'] =\
   480         stats['location_info'] =\
   352             ('ZFSVolumeDriver:%(hostname)s:%(zfs_volume_base)s' %
   481             ('ZFSVolumeDriver:%(hostname)s:%(zfs_volume_base)s:local' %
   353              {'hostname': self.hostname,
   482              {'hostname': self.hostname,
   354               'zfs_volume_base': self.configuration.zfs_volume_base})
   483               'zfs_volume_base': self.configuration.zfs_volume_base})
   355 
   484 
   356         self._stats = stats
   485         self._stats = stats
   357 
   486 
   371         cmd = ['/usr/sbin/zfs', 'rename', src, dst]
   500         cmd = ['/usr/sbin/zfs', 'rename', src, dst]
   372         self._execute(*cmd)
   501         self._execute(*cmd)
   373 
   502 
   374         LOG.debug(_("Rename the volume '%s' to '%s'") % (src, dst))
   503         LOG.debug(_("Rename the volume '%s' to '%s'") % (src, dst))
   375 
   504 
       
   505     def _get_existing_volume_ref_name(self, existing_ref):
       
   506         """Returns the volume name of an existing reference.
       
   507         And Check if an existing volume reference has a source-name
       
   508         """
       
   509         if 'source-name' in existing_ref:
       
   510             vol_name = existing_ref['source-name']
       
   511             return vol_name
       
   512         else:
       
   513             reason = _("Reference must contain source-name.")
       
   514             raise exception.ManageExistingInvalidReference(
       
   515                 existing_ref=existing_ref,
       
   516                 reason=reason)
       
   517 
       
   518     def manage_existing_get_size(self, volume, existing_ref):
       
   519         """Return size of volume to be managed by manage_existing.
       
   520         existing_ref is a dictionary of the form:
       
   521         {'source-name': <name of the volume>}
       
   522         """
       
   523         target_vol_name = self._get_existing_volume_ref_name(existing_ref)
       
   524         volsize = self._get_zfs_property('volsize', target_vol_name)
       
   525 
       
   526         return Size(volsize).get(Size.gb_units)
       
   527 
       
   528     def manage_existing(self, volume, existing_ref):
       
   529         """Brings an existing zfs volume object under Cinder management.
       
   530 
       
   531         :param volume:       Cinder volume to manage
       
   532         :param existing_ref: Driver-specific information used to identify a
       
   533         volume
       
   534         """
       
   535         # Check the existence of the ZFS volume
       
   536         target_vol_name = self._get_existing_volume_ref_name(existing_ref)
       
   537         prop_type = self._get_zfs_property('type', target_vol_name)
       
   538         if prop_type != 'volume':
       
   539             msg = (_("Failed to identify the volume '%s'.")
       
   540                    % target_vol_name)
       
   541             raise exception.InvalidInput(reason=msg)
       
   542 
       
   543         if volume['name']:
       
   544             volume_name = volume['name']
       
   545         else:
       
   546             volume_name = 'new_zvol'
       
   547 
       
   548         # rename the volume
       
   549         dst_volume = "%s/%s" % (self.configuration.zfs_volume_base,
       
   550                                 volume_name)
       
   551         self.rename_volume(target_vol_name, dst_volume)
       
   552 
       
   553     def unmanage(self, volume):
       
   554         """Removes the specified volume from Cinder management."""
       
   555         # Rename the volume's name to cinder-unm-* format.
       
   556         volume_name = self._get_zfs_volume_name(volume['name'])
       
   557         tmp_volume_name = "cinder-unm-%s" % volume['name']
       
   558         new_volume_name = "%s/%s" % (self.configuration.zfs_volume_base,
       
   559                                      tmp_volume_name)
       
   560         self.rename_volume(volume_name, new_volume_name)
       
   561 
   376     def migrate_volume(self, context, volume, host):
   562     def migrate_volume(self, context, volume, host):
   377         """Migrate the volume among different backends on the same server.
   563         """Migrate the volume from one backend to another one.
   378 
   564         The backends should be in the same volume type.
   379         The volume migration can only run locally by calling zfs send/recv
   565 
   380         cmds and the specified host needs to be on the same server with the
       
   381         host. But, one exception is when the src and dst volume are located
       
   382         under the same zpool locally or remotely, the migration will be done
       
   383         by just renaming the volume.
       
   384         :param context: context
   566         :param context: context
   385         :param volume: a dictionary describing the volume to migrate
   567         :param volume: a dictionary describing the volume to migrate
   386         :param host: a dictionary describing the host to migrate to
   568         :param host: a dictionary describing the host to migrate to
   387         """
   569         """
   388         false_ret = (False, None)
   570         false_ret = (False, None)
   389         if volume['status'] != 'available':
   571         if volume['status'] != 'available':
   390             LOG.debug(_("Status of volume '%s' is '%s', not 'available'.") %
   572             LOG.debug(_("Status of volume '%s' is '%s', not 'available'.") %
   391                       (volume['name'], volume['status']))
   573                       (volume['name'], volume['status']))
   392             return false_ret
   574             return false_ret
   393 
   575 
   394         if 'capabilities' not in host or \
   576         if 'capabilities' not in host:
   395            'location_info' not in host['capabilities']:
   577             LOG.debug(("No 'capabilities' is reported in the host'%s'") %
   396             LOG.debug(_("No location_info or capabilities are in host info"))
   578                       host['host'])
   397             return false_ret
   579             return false_ret
   398 
   580 
       
   581         if 'location_info' not in host['capabilities']:
       
   582             LOG.debug(("No 'location_info' is reported in the host'%s'") %
       
   583                       host['host'])
       
   584             return false_ret
       
   585 
   399         info = host['capabilities']['location_info']
   586         info = host['capabilities']['location_info']
   400         if (self.hostname != info.split(':')[1]):
   587         dst_volume = "%s/%s" % (info.split(':')[2], volume['name'])
   401             LOG.debug(_("Migration between two different servers '%s' and "
       
   402                       "'%s' is not supported yet.") %
       
   403                       (self.hostname, info.split(':')[1]))
       
   404             return false_ret
       
   405 
       
   406         dst_volume = "%s/%s" % (info.split(':')[-1], volume['name'])
       
   407         src_volume = self._get_zfs_volume_name(volume['name'])
   588         src_volume = self._get_zfs_volume_name(volume['name'])
       
   589 
   408         # check if the src and dst volume are under the same zpool
   590         # check if the src and dst volume are under the same zpool
   409         if (src_volume.split('/')[0] == dst_volume.split('/')[0]):
   591         dst_san_info = info.split(':')[3]
   410             self.rename_volume(src_volume, dst_volume)
   592         if dst_san_info == 'local':
   411         else:
       
   412             self._zfs_send_recv(volume, dst_volume)
   593             self._zfs_send_recv(volume, dst_volume)
   413             # delete the source volume
   594         else:
   414             self.delete_volume(volume)
   595             self._rad_zfs_send_recv(volume, dst_volume, dst_san_info)
       
   596         # delete the source volume
       
   597         self.delete_volume(volume)
   415 
   598 
   416         provider_location = {}
   599         provider_location = {}
   417         return (True, provider_location)
   600         return (True, provider_location)
   418 
   601 
   419 
   602 
   542     """ZFS volume operations in iSCSI mode."""
   725     """ZFS volume operations in iSCSI mode."""
   543     protocol = 'iSCSI'
   726     protocol = 'iSCSI'
   544 
   727 
   545     def __init__(self, *args, **kwargs):
   728     def __init__(self, *args, **kwargs):
   546         super(ZFSISCSIDriver, self).__init__(*args, **kwargs)
   729         super(ZFSISCSIDriver, self).__init__(*args, **kwargs)
       
   730         if not self.configuration.san_is_local:
       
   731             self.hostname, alias, addresslist = \
       
   732                 socket.gethostbyaddr(self.configuration.san_ip)
       
   733 
       
   734     def get_volume_stats(self, refresh=False):
       
   735         """Get volume status."""
       
   736         status = super(ZFSISCSIDriver, self).get_volume_stats(refresh)
       
   737         status["storage_protocol"] = self.protocol
       
   738         backend_name = self.configuration.safe_get('volume_backend_name')
       
   739         status["volume_backend_name"] = backend_name or self.__class__.__name__
       
   740 
       
   741         if not self.configuration.san_is_local:
       
   742             san_info = "%s;%s;%s" % (self.configuration.san_ip,
       
   743                                      self.configuration.san_login,
       
   744                                      self.configuration.san_password)
       
   745             status['location_info'] = \
       
   746                 ('ZFSISCSIDriver:%(hostname)s:%(zfs_volume_base)s:'
       
   747                  '%(san_info)s' %
       
   748                  {'hostname': self.hostname,
       
   749                   'zfs_volume_base': self.configuration.zfs_volume_base,
       
   750                   'san_info': san_info})
       
   751 
       
   752         return status
   547 
   753 
   548     def do_setup(self, context):
   754     def do_setup(self, context):
   549         """Setup the target and target group."""
   755         """Setup the target and target group."""
   550         target_group = self.configuration.zfs_target_group
   756         target_group = self.configuration.zfs_target_group
   551         target_name = '%s%s-%s-target' % \
   757         target_name = '%s%s-%s-target' % \
   733     """ZFS volume operations in FC mode."""
   939     """ZFS volume operations in FC mode."""
   734     protocol = 'FC'
   940     protocol = 'FC'
   735 
   941 
   736     def __init__(self, *args, **kwargs):
   942     def __init__(self, *args, **kwargs):
   737         super(ZFSFCDriver, self).__init__(*args, **kwargs)
   943         super(ZFSFCDriver, self).__init__(*args, **kwargs)
       
   944         if not self.configuration.san_is_local:
       
   945             self.hostname, alias, addresslist = \
       
   946                 socket.gethostbyaddr(self.configuration.san_ip)
       
   947 
       
   948     def get_volume_stats(self, refresh=False):
       
   949         """Get volume status."""
       
   950         status = super(ZFSFCDriver, self).get_volume_stats(refresh)
       
   951         status["storage_protocol"] = self.protocol
       
   952         backend_name = self.configuration.safe_get('volume_backend_name')
       
   953         status["volume_backend_name"] = backend_name or self.__class__.__name__
       
   954 
       
   955         if not self.configuration.san_is_local:
       
   956             san_info = "%s;%s;%s" % (self.configuration.san_ip,
       
   957                                      self.configuration.san_login,
       
   958                                      self.configuration.san_password)
       
   959             status['location_info'] = \
       
   960                 ('ZFSFCDriver:%(hostname)s:%(zfs_volume_base)s:'
       
   961                  '%(san_info)s' %
       
   962                  {'hostname': self.hostname,
       
   963                   'zfs_volume_base': self.configuration.zfs_volume_base,
       
   964                   'san_info': san_info})
       
   965 
       
   966         return status
   738 
   967 
   739     def check_for_setup_error(self):
   968     def check_for_setup_error(self):
   740         """Check the setup error."""
   969         """Check the setup error."""
   741         wwns = self._get_wwns()
   970         wwns = self._get_wwns()
   742         if not wwns:
   971         if not wwns:
   845             # Enable the target and add it to the 'tg-wwn-xxx' group
  1074             # Enable the target and add it to the 'tg-wwn-xxx' group
   846             self._stmf_execute('/usr/sbin/stmfadm', 'offline-target',
  1075             self._stmf_execute('/usr/sbin/stmfadm', 'offline-target',
   847                                'wwn.%s' % wwn)
  1076                                'wwn.%s' % wwn)
   848             self._stmf_execute('/usr/sbin/stmfadm', 'add-tg-member', '-g',
  1077             self._stmf_execute('/usr/sbin/stmfadm', 'add-tg-member', '-g',
   849                                target_group, 'wwn.%s' % wwn)
  1078                                target_group, 'wwn.%s' % wwn)
   850             self._stmf_execute('/usr/sbin/stmfadm', 'online-target',
       
   851                                'wwn.%s' % wwn)
       
   852         assert self._target_in_tg(wwn, target_group)
       
   853 
  1079 
   854         # Add a logical unit view entry
  1080         # Add a logical unit view entry
   855         # TODO(Strony): replace the auto assigned LUN with '-n' option
  1081         # TODO(Strony): replace the auto assigned LUN with '-n' option
   856         if luid is not None:
  1082         if luid is not None:
   857             self._stmf_execute('/usr/sbin/stmfadm', 'add-view', '-t',
  1083             self._stmf_execute('/usr/sbin/stmfadm', 'add-view', '-t',
   858                                target_group, luid)
  1084                                target_group, luid)
       
  1085             self._stmf_execute('/usr/sbin/stmfadm', 'online-target',
       
  1086                                'wwn.%s' % wwn)
       
  1087         assert self._target_in_tg(wwn, target_group)
   859 
  1088 
   860     def remove_export(self, context, volume):
  1089     def remove_export(self, context, volume):
   861         """Remove an export for a volume."""
  1090         """Remove an export for a volume."""
   862         luid = self._get_luid(volume)
  1091         luid = self._get_luid(volume)
   863 
  1092