1 # vim: tabstop=4 shiftwidth=4 softtabstop=4 |
|
2 |
|
3 # Copyright (c) 2015, 2016, 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 Fibre Channel 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 SolarisFibreChannel(object): |
|
33 def __init__(self, *args, **kwargs): |
|
34 self.execute = putils.execute |
|
35 |
|
36 def _get_fc_hbas(self): |
|
37 """Get Fibre Channel HBA information.""" |
|
38 out = None |
|
39 try: |
|
40 out, err = self.execute('/usr/sbin/fcinfo', 'hba-port') |
|
41 except putils.ProcessExecutionError as err: |
|
42 return None |
|
43 |
|
44 if out is None: |
|
45 LOG.info(_("Cannot find any Fibre Channel HBAs")) |
|
46 return None |
|
47 |
|
48 hbas = [] |
|
49 hba = {} |
|
50 for line in out.splitlines(): |
|
51 line = line.strip() |
|
52 # Collect the following hba-port data: |
|
53 # 1: Port WWN |
|
54 # 2: State (online|offline) |
|
55 # 3: Node WWN |
|
56 if line.startswith("HBA Port WWN:"): |
|
57 # New HBA port entry |
|
58 hba = {} |
|
59 wwpn = line.split()[-1] |
|
60 hba['port_name'] = wwpn |
|
61 continue |
|
62 elif line.startswith("Port Mode:"): |
|
63 mode = line.split()[-1] |
|
64 # Skip Target mode ports |
|
65 if mode != 'Initiator': |
|
66 break |
|
67 elif line.startswith("State:"): |
|
68 state = line.split()[-1] |
|
69 hba['port_state'] = state |
|
70 continue |
|
71 elif line.startswith("Node WWN:"): |
|
72 wwnn = line.split()[-1] |
|
73 hba['node_name'] = wwnn |
|
74 continue |
|
75 if len(hba) == 3: |
|
76 hbas.append(hba) |
|
77 hba = {} |
|
78 return hbas |
|
79 |
|
80 def get_fc_wwnns(self): |
|
81 """Get Fibre Channel WWNNs from the system, if any.""" |
|
82 hbas = self._get_fc_hbas() |
|
83 if hbas is None: |
|
84 return None |
|
85 |
|
86 wwnns = [] |
|
87 for hba in hbas: |
|
88 if hba['port_state'] == 'online': |
|
89 wwnn = hba['node_name'] |
|
90 wwnns.append(wwnn) |
|
91 return wwnns |
|
92 |
|
93 def get_fc_wwpns(self): |
|
94 """Get Fibre Channel WWPNs from the system, if any.""" |
|
95 hbas = self._get_fc_hbas() |
|
96 if hbas is None: |
|
97 return None |
|
98 |
|
99 wwpns = [] |
|
100 for hba in hbas: |
|
101 if hba['port_state'] == 'online': |
|
102 wwpn = hba['port_name'] |
|
103 wwpns.append(wwpn) |
|
104 return wwpns |
|
105 |
|
106 def _refresh_connection(self): |
|
107 """Force the link reinitialization to make the LUN present.""" |
|
108 wwpns = self.get_fc_wwpns() |
|
109 for wwpn in wwpns: |
|
110 self.execute('/usr/sbin/fcadm', 'force-lip', wwpn) |
|
111 |
|
112 def _get_device_path(self, wwn, target_lun): |
|
113 """Get the Device path for the specified LUN. |
|
114 |
|
115 The output of CMD below is like this: |
|
116 |
|
117 OS Device Name: /dev/rdsk/c0t600C0FF0000000000036223AE73EB705d0s2 |
|
118 HBA Port WWN: 210100e08b27a8a1 |
|
119 Remote Port WWN: 256000c0ffc03622 |
|
120 LUN: 0 |
|
121 Remote Port WWN: 216000c0ff803622 |
|
122 LUN: 0 |
|
123 HBA Port WWN: 210000e08b07a8a1 |
|
124 Remote Port WWN: 256000c0ffc03622 |
|
125 LUN: 0 |
|
126 Remote Port WWN: 216000c0ff803622 |
|
127 LUN: 0 |
|
128 Vendor: SUN |
|
129 Product: StorEdge 3510 |
|
130 Device Type: Disk device |
|
131 ...... |
|
132 """ |
|
133 try: |
|
134 out, err = self.execute('/usr/sbin/fcinfo', 'logical-unit', '-v') |
|
135 except putils.ProcessExecutionError as err: |
|
136 return None |
|
137 |
|
138 host_device = None |
|
139 remote_port = None |
|
140 if out is not None: |
|
141 for line in [l.strip() for l in out.splitlines()]: |
|
142 if line.startswith("OS Device Name:"): |
|
143 host_device = line.split()[-1] |
|
144 if line.startswith("Remote Port WWN:"): |
|
145 remote_port = line.split()[-1] |
|
146 if line.startswith("LUN:"): |
|
147 lun = line.split()[-1] |
|
148 if remote_port.upper() == wwn and \ |
|
149 int(lun) == int(target_lun): |
|
150 return host_device |
|
151 |
|
152 return None |
|
153 |
|
154 def connect_volume(self, connection_properties, scan_tries): |
|
155 """Attach the volume to instance_name. |
|
156 |
|
157 connection_properties for Fibre Channel must include: |
|
158 target_wwn - Specified port WWNs |
|
159 target_lun - LUN id of the volume |
|
160 """ |
|
161 device_info = {'type': 'block'} |
|
162 target_wwn = connection_properties['target_wwn'] |
|
163 target_lun = connection_properties['target_lun'] |
|
164 wwns = [] |
|
165 if isinstance(target_wwn, list): |
|
166 wwns = target_wwn |
|
167 else: |
|
168 wwns.append(target_wwn) |
|
169 |
|
170 # The scsi_vhci disk node is not always present immediately. |
|
171 # Sometimes we need to reinitialize the connection to trigger |
|
172 # a refresh. |
|
173 for i in range(1, scan_tries): |
|
174 # initiator needs time to refresh the LU list |
|
175 time.sleep(i * 2) |
|
176 host_device = self._get_device_path(wwns[0], target_lun) |
|
177 |
|
178 if host_device is not None and os.path.exists(host_device): |
|
179 break |
|
180 else: |
|
181 self._refresh_connection() |
|
182 else: |
|
183 msg = _("Fibre Channel volume device not found.") |
|
184 LOG.error(msg) |
|
185 raise exception.NoFibreChannelVolumeDeviceFound() |
|
186 |
|
187 # Set the label EFI to the disk on SPARC before it is accessed and |
|
188 # make sure the correct device path with slice 0 |
|
189 # (like '/dev/rdsk/c0t600xxxd0s0'). |
|
190 if platform.processor() == 'sparc': |
|
191 tmp_dev_name = host_device.rsplit('s', 1) |
|
192 disk_name = tmp_dev_name[0].split('/')[-1] |
|
193 (out, _err) = self.execute('/usr/sbin/format', '-L', 'efi', '-d', |
|
194 disk_name) |
|
195 host_device = '%ss0' % tmp_dev_name[0] |
|
196 |
|
197 device_info['path'] = host_device |
|
198 return device_info |
|