|
1 # vim: tabstop=4 shiftwidth=4 softtabstop=4 |
|
2 |
|
3 # Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. |
|
4 # |
|
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
6 # not use this file except in compliance with the License. You may obtain |
|
7 # a copy of the License at |
|
8 # |
|
9 # http://www.apache.org/licenses/LICENSE-2.0 |
|
10 # |
|
11 # Unless required by applicable law or agreed to in writing, software |
|
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|
14 # License for the specific language governing permissions and limitations |
|
15 # under the License. |
|
16 |
|
17 """Generic Solaris iSCSI utilities.""" |
|
18 |
|
19 import os |
|
20 import time |
|
21 |
|
22 from cinder.brick import exception |
|
23 from cinder.openstack.common.gettextutils import _ |
|
24 from cinder.openstack.common import log as logging |
|
25 from cinder.openstack.common import processutils as putils |
|
26 |
|
27 LOG = logging.getLogger(__name__) |
|
28 |
|
29 |
|
30 class SolarisiSCSI(object): |
|
31 def __init__(self, *args, **kwargs): |
|
32 self.execute = putils.execute |
|
33 |
|
34 def disconnect_iscsi(self): |
|
35 """Disable the iSCSI discovery method to detach the volume |
|
36 from instance_name. |
|
37 """ |
|
38 self.execute('/usr/sbin/iscsiadm', 'modify', 'discovery', |
|
39 '--sendtargets', 'disable') |
|
40 |
|
41 def _get_device_path(self, connection_properties): |
|
42 """Get the device path from the target info.""" |
|
43 (out, _err) = self.execute('/usr/sbin/iscsiadm', 'list', |
|
44 'target', '-S', |
|
45 connection_properties['target_iqn']) |
|
46 |
|
47 for line in [l.strip() for l in out.splitlines()]: |
|
48 if line.startswith("OS Device Name:"): |
|
49 dev_path = line.split()[-1] |
|
50 return dev_path |
|
51 else: |
|
52 LOG.error(_("No device is found for the target %s.") % |
|
53 connection_properties['target_iqn']) |
|
54 raise |
|
55 |
|
56 def get_initiator(self): |
|
57 """Return the iSCSI initiator node name IQN""" |
|
58 out, err = self.execute('/usr/sbin/iscsiadm', 'list', 'initiator-node') |
|
59 |
|
60 # Sample first line of command output: |
|
61 # Initiator node name: iqn.1986-03.com.sun:01:e00000000000.4f757217 |
|
62 initiator_name_line = out.splitlines()[0] |
|
63 return initiator_name_line.rsplit(' ', 1)[1] |
|
64 |
|
65 def _connect_to_iscsi_portal(self, connection_properties): |
|
66 # TODO(Strony): handle the CHAP authentication |
|
67 target_ip = connection_properties['target_portal'].split(":")[0] |
|
68 self.execute('/usr/sbin/iscsiadm', 'add', 'discovery-address', |
|
69 target_ip) |
|
70 self.execute('/usr/sbin/iscsiadm', 'modify', 'discovery', |
|
71 '--sendtargets', 'enable') |
|
72 (out, _err) = self.execute('/usr/sbin/iscsiadm', 'list', |
|
73 'discovery-address', '-v', |
|
74 target_ip) |
|
75 |
|
76 lines = out.splitlines() |
|
77 if not lines[0].strip().startswith('Discovery Address: ') or \ |
|
78 lines[1].strip().startswith('Unable to get targets.'): |
|
79 msg = _("No iSCSI target is found.") |
|
80 LOG.error(msg) |
|
81 raise |
|
82 |
|
83 target_iqn = connection_properties['target_iqn'] |
|
84 for line in [l.strip() for l in lines]: |
|
85 if line.startswith("Target name:") and \ |
|
86 line.split()[-1] == target_iqn: |
|
87 return |
|
88 else: |
|
89 LOG.error(_("No active session is found for the target %s.") % |
|
90 target_iqn) |
|
91 raise |
|
92 |
|
93 def connect_volume(self, connection_properties, scan_tries): |
|
94 """Attach the volume to instance_name. |
|
95 |
|
96 connection_properties for iSCSI must include: |
|
97 target_portal - ip and optional port |
|
98 target_iqn - iSCSI Qualified Name |
|
99 target_lun - LUN id of the volume |
|
100 """ |
|
101 device_info = {'type': 'block'} |
|
102 |
|
103 # TODO(Strony): support the iSCSI multipath on Solaris. |
|
104 self._connect_to_iscsi_portal(connection_properties) |
|
105 |
|
106 host_device = self._get_device_path(connection_properties) |
|
107 |
|
108 # check if it is a valid device path. |
|
109 for i in range(1, scan_tries): |
|
110 if os.path.exists(host_device): |
|
111 break |
|
112 else: |
|
113 time.sleep(i ** 2) |
|
114 else: |
|
115 raise exception.VolumeDeviceNotFound(device=host_device) |
|
116 |
|
117 device_info['path'] = host_device |
|
118 return device_info |