components/openstack/cinder/files/solaris/zfs.py
branchs11u3-sru
changeset 5431 a515b642a711
parent 5430 b6b088be89d5
child 6035 c9748fcc32de
equal deleted inserted replaced
5430:b6b088be89d5 5431:a515b642a711
    25 import socket
    25 import socket
    26 import subprocess
    26 import subprocess
    27 import time
    27 import time
    28 
    28 
    29 from oslo.config import cfg
    29 from oslo.config import cfg
       
    30 import paramiko
    30 
    31 
    31 from cinder import exception
    32 from cinder import exception
    32 from cinder.i18n import _
    33 from cinder.i18n import _
    33 from cinder.image import image_utils
    34 from cinder.image import image_utils
    34 from cinder.openstack.common import log as logging
    35 from cinder.openstack.common import log as logging
   111                                    "from that of the source volume, '%s'.")
   112                                    "from that of the source volume, '%s'.")
   112                                  % (volume['name'], volume['size'],
   113                                  % (volume['name'], volume['size'],
   113                                     src_vref['size']))
   114                                     src_vref['size']))
   114             raise exception.VolumeBackendAPIException(data=exception_message)
   115             raise exception.VolumeBackendAPIException(data=exception_message)
   115 
   116 
   116         src_volume_name = src_vref['name']
   117         self._zfs_send_recv(src_vref,
   117         volume_name = volume['name']
   118                             self._get_zfs_volume_name(volume['name']))
   118         tmp_snapshot = {'volume_name': src_volume_name,
       
   119                         'name': 'tmp-snapshot-%s' % volume['id']}
       
   120         self.create_snapshot(tmp_snapshot)
       
   121 
       
   122         # Create a ZFS clone
       
   123         zfs_snapshot = self._get_zfs_snap_name(tmp_snapshot)
       
   124         zfs_volume = self._get_zfs_volume_name(volume['name'])
       
   125         cmd = ['/usr/sbin/zfs', 'clone', zfs_snapshot, zfs_volume]
       
   126         self._execute(*cmd)
       
   127 
   119 
   128         LOG.debug(_("Created cloned volume '%s' from source volume '%s'")
   120         LOG.debug(_("Created cloned volume '%s' from source volume '%s'")
   129                   % (volume_name, src_volume_name))
   121                   % (volume['name'], src_vref['name']))
   130 
   122 
   131     def delete_volume(self, volume):
   123     def delete_volume(self, volume):
   132         """Delete a volume.
   124         """Delete a volume.
   133 
   125 
   134         Firstly, the volume should be checked if it is a cloned one. If yes,
   126         Firstly, the volume should be checked if it is a cloned one. If yes,
   249     def _get_zfs_volume_name(self, volume_name):
   241     def _get_zfs_volume_name(self, volume_name):
   250         """Add the pool name to get the ZFS volume."""
   242         """Add the pool name to get the ZFS volume."""
   251         return "%s/%s" % (self.configuration.zfs_volume_base,
   243         return "%s/%s" % (self.configuration.zfs_volume_base,
   252                           volume_name)
   244                           volume_name)
   253 
   245 
       
   246     def _remote_piped_execute(self, cmd1, cmd2, ip, username, password):
       
   247         """Piped execute on a remote host."""
       
   248         LOG.debug(_("Piping cmd1='%s' into cmd='%s' on host '%s'") %
       
   249                   (' '.join(cmd1), ' '.join(cmd2), ip))
       
   250 
       
   251         client = paramiko.SSHClient()
       
   252         client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
       
   253         client.connect(ip, username=username, password=password)
       
   254 
       
   255         cmd = ' '.join(cmd1) + '|' + ' '.join(cmd2)
       
   256         stdin, stdout, stderr = client.exec_command(cmd)
       
   257         channel = stdout.channel
       
   258         exit_status = channel.recv_exit_status()
       
   259 
       
   260         if exit_status != 0:
       
   261             LOG.error(_("_remote_piped_execute: failed to host '%s' with "
       
   262                         "stdout '%s' and stderr '%s'")
       
   263                       % (ip, stdout.read(), stderr.read()))
       
   264             msg = (_("Remote piped execution failed to host '%s'.") % ip)
       
   265             raise exception.VolumeBackendAPIException(data=msg)
       
   266 
   254     def _piped_execute(self, cmd1, cmd2):
   267     def _piped_execute(self, cmd1, cmd2):
   255         """Pipe output of cmd1 into cmd2."""
   268         """Pipe output of cmd1 into cmd2."""
   256         LOG.debug(_("Piping cmd1='%s' into cmd2='%s'") %
   269         LOG.debug(_("Piping cmd1='%s' into cmd2='%s'") %
   257                   (' '.join(cmd1), ' '.join(cmd2)))
   270                   (' '.join(cmd1), ' '.join(cmd2)))
   258 
   271 
   276         if p2.returncode:
   289         if p2.returncode:
   277             msg = (_("_piped_execute failed with the info '%s' and '%s'.") %
   290             msg = (_("_piped_execute failed with the info '%s' and '%s'.") %
   278                    (stdout, stderr))
   291                    (stdout, stderr))
   279             raise exception.VolumeBackendAPIException(data=msg)
   292             raise exception.VolumeBackendAPIException(data=msg)
   280 
   293 
   281     def _zfs_send_recv(self, src, dst, remote=False):
   294     def _zfs_send_recv(self, src, dst):
   282         """Replicate the ZFS dataset by calling zfs send/recv cmd"""
   295         """Replicate the ZFS dataset by calling zfs send/recv cmd"""
   283         src_snapshot = {'volume_name': src['name'],
   296         src_snapshot = {'volume_name': src['name'],
   284                         'name': 'tmp-snapshot-%s' % src['id']}
   297                         'name': 'tmp-snapshot-%s' % src['id']}
   285         src_snapshot_name = self._get_zfs_snap_name(src_snapshot)
   298         src_snapshot_name = self._get_zfs_snap_name(src_snapshot)
   286         prop_type = self._get_zfs_property('type', src_snapshot_name)
   299         prop_type = self._get_zfs_property('type', src_snapshot_name)
   291         self.create_snapshot(src_snapshot)
   304         self.create_snapshot(src_snapshot)
   292         src_snapshot_name = self._get_zfs_snap_name(src_snapshot)
   305         src_snapshot_name = self._get_zfs_snap_name(src_snapshot)
   293 
   306 
   294         cmd1 = ['/usr/sbin/zfs', 'send', src_snapshot_name]
   307         cmd1 = ['/usr/sbin/zfs', 'send', src_snapshot_name]
   295         cmd2 = ['/usr/sbin/zfs', 'receive', dst]
   308         cmd2 = ['/usr/sbin/zfs', 'receive', dst]
   296         self._piped_execute(cmd1, cmd2)
   309         # Due to pipe injection protection in the ssh utils method,
       
   310         # cinder.utils.check_ssh_injection(), the piped commands must be passed
       
   311         # through via paramiko.  These commands take no user defined input
       
   312         # other than the names of the zfs datasets which are already protected
       
   313         # against the special characters of concern.
       
   314         if not self.run_local:
       
   315             ip = self.configuration.san_ip
       
   316             username = self.configuration.san_login
       
   317             password = self.configuration.san_password
       
   318             self._remote_piped_execute(cmd1, cmd2, ip, username, password)
       
   319         else:
       
   320             self._piped_execute(cmd1, cmd2)
   297 
   321 
   298         # Delete the temporary src snapshot and dst snapshot
   322         # Delete the temporary src snapshot and dst snapshot
   299         self.delete_snapshot(src_snapshot)
   323         self.delete_snapshot(src_snapshot)
   300         dst_snapshot_name = "%[email protected]%s" % (dst, src['id'])
   324         dst_snapshot_name = "%[email protected]%s" % (dst, src['id'])
   301         cmd = ['/usr/sbin/zfs', 'destroy', dst_snapshot_name]
   325         cmd = ['/usr/sbin/zfs', 'destroy', dst_snapshot_name]
   551 
   575 
   552     def create_export(self, context, volume):
   576     def create_export(self, context, volume):
   553         """Export the volume."""
   577         """Export the volume."""
   554         # If the volume is already exported there is nothing to do, as we
   578         # If the volume is already exported there is nothing to do, as we
   555         # simply export volumes and they are universally available.
   579         # simply export volumes and they are universally available.
   556         if self._get_luid(volume):
   580         luid = self._get_luid(volume)
   557             return
   581         if luid:
       
   582             view_lun = self._get_view_and_lun(luid)
       
   583             if view_lun['view'] is not None:
       
   584                 return
       
   585             else:
       
   586                 msg = (_("Failed to create logical unit for volume '%s' due "
       
   587                          "to an existing LU id but no view.") % volume['name'])
       
   588                 raise exception.VolumeBackendAPIException(data=msg)
   558 
   589 
   559         zvol = self._get_zvol_path(volume)
   590         zvol = self._get_zvol_path(volume)
   560 
   591 
   561         # Create a Logical Unit (LU)
   592         # Create a Logical Unit (LU)
   562         self._stmf_execute('/usr/sbin/stmfadm', 'create-lu', zvol)
   593         self._stmf_execute('/usr/sbin/stmfadm', 'create-lu', zvol)
   772         LOG.debug(_("The target '%s' is not in any target group.") % target)
   803         LOG.debug(_("The target '%s' is not in any target group.") % target)
   773         return False
   804         return False
   774 
   805 
   775     def create_export(self, context, volume):
   806     def create_export(self, context, volume):
   776         """Export the volume."""
   807         """Export the volume."""
       
   808         # If the volume is already exported there is nothing to do, as we
       
   809         # simply export volumes and they are universally available.
       
   810         luid = self._get_luid(volume)
       
   811         if luid:
       
   812             view_lun = self._get_view_and_lun(luid)
       
   813             if view_lun['view'] is not None:
       
   814                 return
       
   815             else:
       
   816                 msg = (_("Failed to create logical unit for volume '%s' due "
       
   817                          "to an existing LU id but no view.") % volume['name'])
       
   818                 raise exception.VolumeBackendAPIException(data=msg)
       
   819 
   777         zvol = self._get_zvol_path(volume)
   820         zvol = self._get_zvol_path(volume)
   778 
   821 
   779         # Create a Logical Unit (LU)
   822         # Create a Logical Unit (LU)
   780         self._stmf_execute('/usr/sbin/stmfadm', 'create-lu', zvol)
   823         self._stmf_execute('/usr/sbin/stmfadm', 'create-lu', zvol)
   781         luid = self._get_luid(volume)
   824         luid = self._get_luid(volume)