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, Oracle and/or its affiliates. All rights reserved. |
5 # Copyright (c) 2014, 2015, 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 # |
18 """ |
18 """ |
19 Drivers for Solaris ZFS operations in local and iSCSI modes |
19 Drivers for Solaris ZFS operations in local and iSCSI modes |
20 """ |
20 """ |
21 |
21 |
22 import abc |
22 import abc |
23 import os |
23 import time |
24 |
24 |
25 from oslo.config import cfg |
25 from oslo.config import cfg |
26 |
26 |
27 from cinder import exception |
27 from cinder import exception |
28 from cinder.image import image_utils |
28 from cinder.image import image_utils |
|
29 from cinder.i18n import _ |
29 from cinder.openstack.common import log as logging |
30 from cinder.openstack.common import log as logging |
30 from cinder.openstack.common import processutils |
31 from cinder.openstack.common import processutils |
31 from cinder.volume import driver |
32 from cinder.volume import driver |
32 |
33 |
33 from solaris_install.target.size import Size |
34 from solaris_install.target.size import Size |
114 Firstly, the volume should be checked if it is a cloned one. If yes, |
115 Firstly, the volume should be checked if it is a cloned one. If yes, |
115 its parent snapshot with prefix 'tmp-snapshot-' should be deleted as |
116 its parent snapshot with prefix 'tmp-snapshot-' should be deleted as |
116 well after it is removed. |
117 well after it is removed. |
117 """ |
118 """ |
118 zvol = self._get_zvol_path(volume) |
119 zvol = self._get_zvol_path(volume) |
119 if not os.path.exists(zvol): |
120 try: |
|
121 (out, _err) = self._execute('/usr/bin/ls', zvol) |
|
122 except processutils.ProcessExecutionError: |
120 LOG.debug(_("The volume path '%s' doesn't exist") % zvol) |
123 LOG.debug(_("The volume path '%s' doesn't exist") % zvol) |
121 return |
124 return |
122 |
125 |
123 zfs_volume = self._get_zfs_volume_name(volume) |
126 zfs_volume = self._get_zfs_volume_name(volume) |
124 origin_snapshot = self._get_zfs_property('origin', zfs_volume) |
127 origin_snapshot = self._get_zfs_property('origin', zfs_volume) |
168 |
171 |
169 def initialize_connection(self, volume, connector): |
172 def initialize_connection(self, volume, connector): |
170 """Initialize the connection and returns connection info.""" |
173 """Initialize the connection and returns connection info.""" |
171 volume_path = '%s/volume-%s' % (self.configuration.zfs_volume_base, |
174 volume_path = '%s/volume-%s' % (self.configuration.zfs_volume_base, |
172 volume['id']) |
175 volume['id']) |
|
176 properties = {} |
|
177 properties['device_path'] = self._get_zvol_path(volume) |
|
178 |
173 return { |
179 return { |
174 'driver_volume_type': 'local', |
180 'driver_volume_type': 'local', |
175 'volume_path': volume_path, |
181 'volume_path': volume_path, |
176 'data': {} |
182 'data': properties |
177 } |
183 } |
178 |
184 |
179 def terminate_connection(self, volume, connector, **kwargs): |
185 def terminate_connection(self, volume, connector, **kwargs): |
180 """Disconnection from the connector.""" |
186 """Disconnection from the connector.""" |
181 pass |
187 pass |
269 """Abstract base class for common COMSTAR operations.""" |
275 """Abstract base class for common COMSTAR operations.""" |
270 __metaclass__ = abc.ABCMeta |
276 __metaclass__ = abc.ABCMeta |
271 |
277 |
272 def __init__(self, *args, **kwargs): |
278 def __init__(self, *args, **kwargs): |
273 super(STMFDriver, self).__init__(*args, **kwargs) |
279 super(STMFDriver, self).__init__(*args, **kwargs) |
|
280 |
|
281 def _stmf_execute(self, *cmd): |
|
282 """Handle the possible race during the local execution.""" |
|
283 tries = 0 |
|
284 while True: |
|
285 try: |
|
286 self._execute(*cmd) |
|
287 return |
|
288 except processutils.ProcessExecutionError as ex: |
|
289 tries = tries + 1 |
|
290 |
|
291 if tries >= self.configuration.num_shell_tries or \ |
|
292 'resource busy' not in ex.stderr: |
|
293 raise |
|
294 |
|
295 time.sleep(tries ** 2) |
274 |
296 |
275 def _check_target(self, target, protocol): |
297 def _check_target(self, target, protocol): |
276 """Verify if the target exists.""" |
298 """Verify if the target exists.""" |
277 (out, _err) = self._execute('/usr/sbin/stmfadm', 'list-target') |
299 (out, _err) = self._execute('/usr/sbin/stmfadm', 'list-target') |
278 |
300 |
359 break |
381 break |
360 if line.startswith("Lun"): |
382 if line.startswith("Lun"): |
361 view_and_lun['lun'] = int(line.split()[2]) |
383 view_and_lun['lun'] = int(line.split()[2]) |
362 |
384 |
363 if view_and_lun['view'] is None or view_and_lun['lun'] is None: |
385 if view_and_lun['view'] is None or view_and_lun['lun'] is None: |
364 LOG.error(_("Failed to get the view_entry or LUN of the LU '%s'.") |
386 err_msg = (_("Failed to get the view_entry or LUN of the LU '%s'.") |
365 % lu) |
387 % lu) |
366 raise |
388 raise exception.VolumeBackendAPIException(data=err_msg) |
367 else: |
389 else: |
368 LOG.debug(_("The view_entry and LUN of LU '%s' are '%s' and '%d'.") |
390 LOG.debug(_("The view_entry and LUN of LU '%s' are '%s' and '%d'.") |
369 % (lu, view_and_lun['view'], view_and_lun['lun'])) |
391 % (lu, view_and_lun['view'], view_and_lun['lun'])) |
370 |
392 |
371 return view_and_lun |
393 return view_and_lun |
381 def create_export(self, context, volume): |
403 def create_export(self, context, volume): |
382 """Export the volume.""" |
404 """Export the volume.""" |
383 zvol = self._get_zvol_path(volume) |
405 zvol = self._get_zvol_path(volume) |
384 |
406 |
385 # Create a Logical Unit (LU) |
407 # Create a Logical Unit (LU) |
386 self._execute('/usr/sbin/stmfadm', 'create-lu', zvol) |
408 self._stmf_execute('/usr/sbin/stmfadm', 'create-lu', zvol) |
387 luid = self._get_luid(volume) |
409 luid = self._get_luid(volume) |
388 if not luid: |
410 if not luid: |
389 msg = (_("Failed to create LU for volume '%s'") |
411 msg = (_("Failed to create LU for volume '%s'") |
390 % volume['name']) |
412 % volume['name']) |
391 raise exception.VolumeBackendAPIException(data=msg) |
413 raise exception.VolumeBackendAPIException(data=msg) |
392 |
414 |
393 # Create a target group and a target belonging to the target group |
415 # Create a target group and a target belonging to the target group |
394 target_group = 'tg-%s' % volume['name'] |
416 target_group = 'tg-%s' % volume['name'] |
395 self._execute('/usr/sbin/stmfadm', 'create-tg', target_group) |
417 self._stmf_execute('/usr/sbin/stmfadm', 'create-tg', target_group) |
396 |
418 |
397 target_name = '%s%s' % (self.configuration.iscsi_target_prefix, |
419 target_name = '%s%s' % (self.configuration.iscsi_target_prefix, |
398 volume['name']) |
420 volume['name']) |
399 self._execute('/usr/sbin/stmfadm', 'add-tg-member', '-g', |
421 self._stmf_execute('/usr/sbin/stmfadm', 'add-tg-member', '-g', |
400 target_group, target_name) |
422 target_group, target_name) |
401 |
423 |
402 self._execute('/usr/sbin/itadm', 'create-target', '-n', target_name) |
424 self._stmf_execute('/usr/sbin/itadm', 'create-target', '-n', |
|
425 target_name) |
403 assert self._check_target(target_name, 'iSCSI') |
426 assert self._check_target(target_name, 'iSCSI') |
404 |
427 |
405 # Add a view entry to the logical unit with the specified LUN, 8776 |
428 # Add a view entry to the logical unit with the specified LUN, 8776 |
406 if luid is not None: |
429 if luid is not None: |
407 self._execute('/usr/sbin/stmfadm', 'add-view', '-n', 8776, '-t', |
430 self._stmf_execute('/usr/sbin/stmfadm', 'add-view', '-n', '8776', |
408 target_group, luid) |
431 '-t', target_group, luid) |
409 |
432 |
410 def remove_export(self, context, volume): |
433 def remove_export(self, context, volume): |
411 """Remove an export for a volume. |
434 """Remove an export for a volume. |
412 |
435 |
413 All of the related elements about the volume, including the |
436 All of the related elements about the volume, including the |
420 |
443 |
421 # Remove the view entry |
444 # Remove the view entry |
422 if luid is not None: |
445 if luid is not None: |
423 view_lun = self._get_view_and_lun(luid) |
446 view_lun = self._get_view_and_lun(luid) |
424 if view_lun['view']: |
447 if view_lun['view']: |
425 self._execute('/usr/sbin/stmfadm', 'remove-view', '-l', |
448 self._stmf_execute('/usr/sbin/stmfadm', 'remove-view', '-l', |
426 luid, view_lun['view']) |
449 luid, view_lun['view']) |
427 |
450 |
428 # Remove the target and its target group |
451 # Remove the target and its target group |
429 if self._check_target(target_name, 'iSCSI'): |
452 if self._check_target(target_name, 'iSCSI'): |
430 self._execute('/usr/sbin/stmfadm', 'offline-target', target_name) |
453 self._stmf_execute('/usr/sbin/stmfadm', 'offline-target', |
431 self._execute('/usr/sbin/itadm', 'delete-target', '-f', |
454 target_name) |
432 target_name) |
455 self._stmf_execute('/usr/sbin/itadm', 'delete-target', '-f', |
|
456 target_name) |
433 |
457 |
434 if self._check_tg(target_group): |
458 if self._check_tg(target_group): |
435 self._execute('/usr/sbin/stmfadm', 'delete-tg', target_group) |
459 self._stmf_execute('/usr/sbin/stmfadm', 'delete-tg', target_group) |
436 |
460 |
437 # Remove the LU |
461 # Remove the LU |
438 if luid is not None: |
462 if luid is not None: |
439 self._execute('/usr/sbin/stmfadm', 'delete-lu', luid) |
463 self._stmf_execute('/usr/sbin/stmfadm', 'delete-lu', luid) |
440 |
464 |
441 def _get_iscsi_properties(self, volume): |
465 def _get_iscsi_properties(self, volume): |
442 """Get iSCSI configuration |
466 """Get iSCSI configuration |
443 |
467 |
444 Now we use the discovery address as the default approach to add |
468 Now we use the discovery address as the default approach to add |
465 volume['name']) |
489 volume['name']) |
466 properties = {} |
490 properties = {} |
467 |
491 |
468 properties['target_discovered'] = True |
492 properties['target_discovered'] = True |
469 properties['target_iqn'] = target_name |
493 properties['target_iqn'] = target_name |
|
494 |
470 properties['target_portal'] = ('%s:%d' % |
495 properties['target_portal'] = ('%s:%d' % |
471 (self.configuration.iscsi_ip_address, |
496 (self.configuration.iscsi_ip_address, |
472 self.configuration.iscsi_port)) |
497 self.configuration.iscsi_port)) |
473 view_lun = self._get_view_and_lun(luid) |
498 view_lun = self._get_view_and_lun(luid) |
474 if view_lun['lun']: |
499 if view_lun['lun'] is not None: |
475 properties['target_lun'] = view_lun['lun'] |
500 properties['target_lun'] = view_lun['lun'] |
476 properties['volume_id'] = volume['id'] |
501 properties['volume_id'] = volume['id'] |
477 |
502 |
478 auth = volume['provider_auth'] |
503 auth = volume['provider_auth'] |
479 if auth: |
504 if auth: |
604 def create_export(self, context, volume): |
629 def create_export(self, context, volume): |
605 """Export the volume.""" |
630 """Export the volume.""" |
606 zvol = self._get_zvol_path(volume) |
631 zvol = self._get_zvol_path(volume) |
607 |
632 |
608 # Create a Logical Unit (LU) |
633 # Create a Logical Unit (LU) |
609 self._execute('/usr/sbin/stmfadm', 'create-lu', zvol) |
634 self._stmf_execute('/usr/sbin/stmfadm', 'create-lu', zvol) |
610 luid = self._get_luid(volume) |
635 luid = self._get_luid(volume) |
611 if not luid: |
636 if not luid: |
612 msg = (_("Failed to create logic unit for volume '%s'") |
637 msg = (_("Failed to create logic unit for volume '%s'") |
613 % volume['name']) |
638 % volume['name']) |
614 raise exception.VolumeBackendAPIException(data=msg) |
639 raise exception.VolumeBackendAPIException(data=msg) |
624 "into the expected target group '%s'.") % |
649 "into the expected target group '%s'.") % |
625 (wwn, target_group)) |
650 (wwn, target_group)) |
626 raise exception.VolumeBackendAPIException(data=msg) |
651 raise exception.VolumeBackendAPIException(data=msg) |
627 |
652 |
628 # Create a target group for the wwn |
653 # Create a target group for the wwn |
629 self._execute('/usr/sbin/stmfadm', 'create-tg', target_group) |
654 self._stmf_execute('/usr/sbin/stmfadm', 'create-tg', target_group) |
630 |
655 |
631 # Enable the target and add it to the 'tg-wwn-xxx' group |
656 # Enable the target and add it to the 'tg-wwn-xxx' group |
632 self._execute('/usr/sbin/stmfadm', 'offline-target', |
657 self._stmf_execute('/usr/sbin/stmfadm', 'offline-target', |
633 'wwn.%s' % wwn) |
658 'wwn.%s' % wwn) |
634 self._execute('/usr/sbin/stmfadm', 'add-tg-member', '-g', |
659 self._stmf_execute('/usr/sbin/stmfadm', 'add-tg-member', '-g', |
635 target_group, 'wwn.%s' % wwn) |
660 target_group, 'wwn.%s' % wwn) |
636 self._execute('/usr/sbin/stmfadm', 'online-target', 'wwn.%s' % wwn) |
661 self._stmf_execute('/usr/sbin/stmfadm', 'online-target', |
|
662 'wwn.%s' % wwn) |
637 assert self._target_in_tg(wwn, target_group) |
663 assert self._target_in_tg(wwn, target_group) |
638 |
664 |
639 # Add a logical unit view entry |
665 # Add a logical unit view entry |
640 # TODO(Strony): replace the auto assigned LUN with '-n' option |
666 # TODO(Strony): replace the auto assigned LUN with '-n' option |
641 if luid is not None: |
667 if luid is not None: |
642 self._execute('/usr/sbin/stmfadm', 'add-view', '-t', |
668 self._stmf_execute('/usr/sbin/stmfadm', 'add-view', '-t', |
643 target_group, luid) |
669 target_group, luid) |
644 |
670 |
645 def remove_export(self, context, volume): |
671 def remove_export(self, context, volume): |
646 """Remove an export for a volume.""" |
672 """Remove an export for a volume.""" |
647 luid = self._get_luid(volume) |
673 luid = self._get_luid(volume) |
648 |
674 |
651 wwn = wwns[0] |
677 wwn = wwns[0] |
652 target_wwn = 'wwn.%s' % wwn |
678 target_wwn = 'wwn.%s' % wwn |
653 target_group = 'tg-wwn-%s' % wwn |
679 target_group = 'tg-wwn-%s' % wwn |
654 view_lun = self._get_view_and_lun(luid) |
680 view_lun = self._get_view_and_lun(luid) |
655 if view_lun['view']: |
681 if view_lun['view']: |
656 self._execute('/usr/sbin/stmfadm', 'remove-view', '-l', |
682 self._stmf_execute('/usr/sbin/stmfadm', 'remove-view', '-l', |
657 luid, view_lun['view']) |
683 luid, view_lun['view']) |
658 |
684 |
659 # Remove the target group when only one LU exists. |
685 # Remove the target group when only one LU exists. |
660 if self._only_lu(luid): |
686 if self._only_lu(luid): |
661 if self._check_target(target_wwn, 'Channel'): |
687 if self._check_target(target_wwn, 'Channel'): |
662 self._execute('/usr/sbin/stmfadm', 'offline-target', |
688 self._stmf_execute('/usr/sbin/stmfadm', 'offline-target', |
663 target_wwn) |
689 target_wwn) |
664 if self._check_tg(target_group): |
690 if self._check_tg(target_group): |
665 self._execute('/usr/sbin/stmfadm', 'delete-tg', |
691 self._stmf_execute('/usr/sbin/stmfadm', 'delete-tg', |
666 target_group) |
692 target_group) |
667 |
693 |
668 # Remove the LU |
694 # Remove the LU |
669 self._execute('/usr/sbin/stmfadm', 'delete-lu', luid) |
695 self._stmf_execute('/usr/sbin/stmfadm', 'delete-lu', luid) |
670 |
696 |
671 def _get_fc_properties(self, volume): |
697 def _get_fc_properties(self, volume): |
672 """Get Fibre Channel configuration. |
698 """Get Fibre Channel configuration. |
673 |
699 |
674 :target_discovered: boolean indicating whether discovery was used |
700 :target_discovered: boolean indicating whether discovery was used |
691 properties = {} |
717 properties = {} |
692 |
718 |
693 properties['target_discovered'] = True |
719 properties['target_discovered'] = True |
694 properties['target_wwn'] = wwns |
720 properties['target_wwn'] = wwns |
695 view_lun = self._get_view_and_lun(luid) |
721 view_lun = self._get_view_and_lun(luid) |
696 if view_lun['lun']: |
722 if view_lun['lun'] is not None: |
697 properties['target_lun'] = view_lun['lun'] |
723 properties['target_lun'] = view_lun['lun'] |
698 return properties |
724 return properties |
699 |
725 |
700 def initialize_connection(self, volume, connector): |
726 def initialize_connection(self, volume, connector): |
701 """Initializes the connection and returns connection info. |
727 """Initializes the connection and returns connection info. |