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 platform |
|
21 import time |
|
22 |
|
23 from oslo_concurrency import processutils as putils |
|
24 from oslo_log import log as logging |
|
25 |
|
26 from cinder.brick import exception |
|
27 from cinder.openstack.common.gettextutils import _ |
|
28 |
|
29 LOG = logging.getLogger(__name__) |
|
30 |
|
31 |
|
32 class SolarisiSCSI(object): |
|
33 def __init__(self, *args, **kwargs): |
|
34 self.execute = putils.execute |
|
35 |
|
36 def _get_device_path(self, connection_properties): |
|
37 """Get the device path from the target info. |
|
38 |
|
39 The output of cmd below is like this: |
|
40 Target: iqn.2010-10.org.openstack:hostname1-tgt-grp-target |
|
41 Alias: - |
|
42 TPGT: 1 |
|
43 ISID: 4000002a0000 |
|
44 Connections: 1 |
|
45 LUN: 1 |
|
46 Vendor: SUN |
|
47 Product: COMSTAR |
|
48 OS Device Name: /dev/rdsk/c0t600144F0FDFAD05D0000563C04030003d0s2 |
|
49 LUN: 0 |
|
50 Vendor: SUN |
|
51 Product: COMSTAR |
|
52 OS Device Name: /dev/rdsk/c0t600144F0FDFAD05D0000563C02270002d0s2 |
|
53 |
|
54 """ |
|
55 (out, _err) = self.execute('/usr/sbin/iscsiadm', 'list', |
|
56 'target', '-S', |
|
57 connection_properties['target_iqn']) |
|
58 |
|
59 found = False |
|
60 for line in [l.strip() for l in out.splitlines()]: |
|
61 if line.startswith("LUN:"): |
|
62 lun = line.split()[-1] |
|
63 if int(lun) == int(connection_properties['target_lun']): |
|
64 found = True |
|
65 continue |
|
66 if found: |
|
67 if line.startswith("OS Device Name:"): |
|
68 dev_path = line.split()[-1] |
|
69 return dev_path |
|
70 elif line.startswith("LUN:"): |
|
71 found = False |
|
72 |
|
73 if not found: |
|
74 LOG.error(_("No device is found for the target %s LUN %s.") % |
|
75 (connection_properties['target_iqn'], |
|
76 connection_properties['target_lun'])) |
|
77 raise |
|
78 |
|
79 def get_initiator(self): |
|
80 """Return the iSCSI initiator node name IQN""" |
|
81 out, err = self.execute('/usr/sbin/iscsiadm', 'list', 'initiator-node') |
|
82 |
|
83 # Sample first line of command output: |
|
84 # Initiator node name: iqn.1986-03.com.sun:01:e00000000000.4f757217 |
|
85 initiator_name_line = out.splitlines()[0] |
|
86 return initiator_name_line.rsplit(' ', 1)[1] |
|
87 |
|
88 def _connect_to_iscsi_portal(self, connection_properties): |
|
89 # TODO(Strony): handle the CHAP authentication |
|
90 target_ip = connection_properties['target_portal'].split(":")[0] |
|
91 self.execute('/usr/sbin/iscsiadm', 'add', 'discovery-address', |
|
92 target_ip) |
|
93 self.execute('/usr/sbin/iscsiadm', 'modify', 'discovery', |
|
94 '--sendtargets', 'enable') |
|
95 (out, _err) = self.execute('/usr/sbin/iscsiadm', 'list', |
|
96 'discovery-address', '-v', |
|
97 target_ip) |
|
98 |
|
99 lines = out.splitlines() |
|
100 if not lines[0].strip().startswith('Discovery Address: ') or \ |
|
101 lines[1].strip().startswith('Unable to get targets.'): |
|
102 msg = _("No iSCSI target is found.") |
|
103 LOG.error(msg) |
|
104 raise |
|
105 |
|
106 target_iqn = connection_properties['target_iqn'] |
|
107 for line in [l.strip() for l in lines]: |
|
108 if line.startswith("Target name:") and \ |
|
109 line.split()[-1] == target_iqn: |
|
110 return |
|
111 else: |
|
112 LOG.error(_("No active session is found for the target %s.") % |
|
113 target_iqn) |
|
114 raise |
|
115 |
|
116 def connect_volume(self, connection_properties, scan_tries): |
|
117 """Attach the volume to instance_name. |
|
118 |
|
119 connection_properties for iSCSI must include: |
|
120 target_portal - ip and optional port |
|
121 target_iqn - iSCSI Qualified Name |
|
122 target_lun - LUN id of the volume |
|
123 """ |
|
124 device_info = {'type': 'block'} |
|
125 |
|
126 # TODO(Strony): support the iSCSI multipath on Solaris. |
|
127 self._connect_to_iscsi_portal(connection_properties) |
|
128 |
|
129 host_device = self._get_device_path(connection_properties) |
|
130 |
|
131 # check if it is a valid device path. |
|
132 for i in range(1, scan_tries): |
|
133 if os.path.exists(host_device): |
|
134 break |
|
135 else: |
|
136 time.sleep(i ** 2) |
|
137 else: |
|
138 raise exception.VolumeDeviceNotFound(device=host_device) |
|
139 |
|
140 # Set the label EFI to the disk on SPARC before it is accessed and |
|
141 # make sure the correct device path with slice 0 |
|
142 # (like '/dev/rdsk/c0t600xxxd0s0'). |
|
143 if platform.processor() == 'sparc': |
|
144 tmp_dev_name = host_device.rsplit('s', 1) |
|
145 disk_name = tmp_dev_name[0].split('/')[-1] |
|
146 (out, _err) = self.execute('/usr/sbin/format', '-L', 'efi', '-d', |
|
147 disk_name) |
|
148 host_device = '%ss0' % tmp_dev_name[0] |
|
149 |
|
150 device_info['path'] = host_device |
|
151 return device_info |
|