summaryrefslogtreecommitdiff
path: root/cinder/volume/drivers/hitachi/hbsd_rest_iscsi.py
blob: 2b3224fdf699ddffbafdc664e75c93286eec43ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# Copyright (C) 2020, 2021, Hitachi, Ltd.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
#
"""REST interface iSCSI module for Hitachi HBSD Driver."""

from oslo_log import log as logging

from cinder.volume.drivers.hitachi import hbsd_rest as rest
from cinder.volume.drivers.hitachi import hbsd_utils as utils

_ISCSI_HMO_REPORT_FULL_PORTAL = 83
_ISCSI_HMO_DISABLE_IO = 91

LOG = logging.getLogger(__name__)
MSG = utils.HBSDMsg


class HBSDRESTISCSI(rest.HBSDREST):
    """REST interface iscsi class for Hitachi HBSD Driver."""

    def _set_target_portal(self, port):
        """Get port info and store it in an instance variable."""
        result = self.client.get_port(port)
        ipv4_addr = result.get('ipv4Address')
        tcp_port = result.get('tcpPort')
        if not ipv4_addr or not tcp_port:
            return False, ipv4_addr, tcp_port
        self.storage_info['portals'][port] = '%(ip)s:%(port)s' % {
            'ip': ipv4_addr,
            'port': tcp_port,
        }
        return True, ipv4_addr, tcp_port

    def connect_storage(self):
        """Prepare for using the storage."""
        target_ports = self.conf.hitachi_target_ports
        compute_target_ports = self.conf.hitachi_compute_target_ports

        super(HBSDRESTISCSI, self).connect_storage()
        # The port type must be ISCSI and the port attributes must contain TAR.
        params = {'portType': 'ISCSI',
                  'portAttributes': 'TAR'}
        port_list = self.client.get_ports(params=params)
        for port in set(target_ports + compute_target_ports):
            if port not in [port_data['portId'] for port_data in port_list]:
                utils.output_log(
                    MSG.INVALID_PORT, port=port, additional_info='(portType, '
                    'portAttributes): not (ISCSI, TAR)')
        for port_data in port_list:
            port = port_data['portId']
            if port not in set(target_ports + compute_target_ports):
                continue
            has_addr = True
            if not port_data['lunSecuritySetting']:
                addr_info = ""
            elif port in set(target_ports + compute_target_ports):
                has_addr, ipv4_addr, tcp_port = self._set_target_portal(port)
                if not has_addr:
                    addr_info = (', ipv4Address: %s, tcpPort: %s' %
                                 (ipv4_addr, tcp_port))
            if not port_data['lunSecuritySetting'] or not has_addr:
                utils.output_log(
                    MSG.INVALID_PORT, port=port,
                    additional_info='portType: %s, lunSecuritySetting: %s%s' %
                    (port_data['portType'], port_data['lunSecuritySetting'],
                     addr_info))
            if not port_data['lunSecuritySetting']:
                continue
            if target_ports and port in target_ports and has_addr:
                self.storage_info['controller_ports'].append(port)
            if (compute_target_ports and port in compute_target_ports and
                    has_addr):
                self.storage_info['compute_ports'].append(port)

        self.check_ports_info()
        utils.output_log(MSG.SET_CONFIG_VALUE,
                         object='port-<IP address:port> list',
                         value=self.storage_info['portals'])

    def create_target_to_storage(self, port, connector, hba_ids):
        """Create an iSCSI target on the specified port."""
        target_name = '%(prefix)s-%(ip)s' % {
            'prefix': self.driver_info['driver_prefix'],
            'ip': connector['ip'],
        }
        body = {'portId': port, 'hostGroupName': target_name}
        if hba_ids:
            body['iscsiName'] = '%(id)s%(suffix)s' % {
                'id': hba_ids,
                'suffix': self.driver_info['target_iqn_suffix'],
            }
        try:
            gid = self.client.add_host_grp(body, no_log=True)
        except Exception:
            params = {'portId': port}
            host_grp_list = self.client.get_host_grps(params)
            for host_grp_data in host_grp_list:
                if host_grp_data['hostGroupName'] == target_name:
                    return target_name, host_grp_data['hostGroupNumber']
            else:
                raise
        return target_name, gid

    def set_hba_ids(self, port, gid, hba_ids):
        """Connect the specified HBA with the specified port."""
        self.client.add_hba_iscsi(port, gid, hba_ids)

    def set_target_mode(self, port, gid):
        """Configure the iSCSI target to meet the environment."""
        body = {'hostMode': 'LINUX/IRIX',
                'hostModeOptions': [_ISCSI_HMO_REPORT_FULL_PORTAL]}
        if self.conf.hitachi_rest_disable_io_wait:
            body['hostModeOptions'].append(_ISCSI_HMO_DISABLE_IO)
        if self.conf.hitachi_host_mode_options:
            for opt in self.conf.hitachi_host_mode_options:
                if int(opt) not in body['hostModeOptions']:
                    body['hostModeOptions'].append(int(opt))
        self.client.modify_host_grp(port, gid, body)

    def _is_host_iqn_registered_in_target(self, port, gid, host_iqn):
        """Check if the specified IQN is registered with iSCSI target."""
        for hba_iscsi in self.client.get_hba_iscsis(port, gid):
            if host_iqn == hba_iscsi['iscsiName']:
                return True
        return False

    def _set_target_info(self, targets, host_grps, iqn):
        """Set the information of the iSCSI target having the specified IQN."""
        for host_grp in host_grps:
            port = host_grp['portId']
            gid = host_grp['hostGroupNumber']
            storage_iqn = host_grp['iscsiName']
            if self._is_host_iqn_registered_in_target(port, gid, iqn):
                targets['info'][port] = True
                targets['list'].append((port, gid))
                targets['iqns'][(port, gid)] = storage_iqn
                return True
        return False

    def _get_host_iqn_registered_in_target_by_name(
            self, port, target_name, host_iqn):
        """Get the information of the iSCSI target having the specified name

        and the specified IQN.
        """
        for hba_iscsi in self.client.get_hba_iscsis_by_name(port, target_name):
            if host_iqn == hba_iscsi['iscsiName']:
                return hba_iscsi
        return None

    def _set_target_info_by_name(self, targets, port, target_name, iqn):
        """Set the information of the iSCSI target having the specified name

        and the specified IQN.
        """
        host_iqn_registered_in_target = (
            self._get_host_iqn_registered_in_target_by_name(
                port, target_name, iqn))
        if host_iqn_registered_in_target:
            gid = host_iqn_registered_in_target['hostGroupNumber']
            storage_iqn = self.client.get_host_grp(port, gid)['iscsiName']
            targets['info'][port] = True
            targets['list'].append((port, gid))
            targets['iqns'][(port, gid)] = storage_iqn
            return True
        return False

    def find_targets_from_storage(self, targets, connector, target_ports):
        """Find mapped ports, memorize them and return unmapped port count."""
        iqn = self.get_hba_ids_from_connector(connector)
        not_found_count = 0
        for port in target_ports:
            targets['info'][port] = False
            if 'ip' in connector:
                target_name = '%(prefix)s-%(ip)s' % {
                    'prefix': self.driver_info['driver_prefix'],
                    'ip': connector['ip'],
                }
                if self._set_target_info_by_name(
                        targets, port, target_name, iqn):
                    continue
            host_grps = self.client.get_host_grps({'portId': port})
            if 'ip' in connector:
                host_grps = [hg for hg in host_grps
                             if hg['hostGroupName'] != target_name]
            if self._set_target_info(targets, host_grps, iqn):
                pass
            else:
                not_found_count += 1
        return not_found_count

    def initialize_connection(self, volume, connector, is_snapshot=False):
        """Initialize connection between the server and the volume."""
        conn_info, map_info = super(HBSDRESTISCSI, self).initialize_connection(
            volume, connector, is_snapshot)
        return conn_info

    def get_properties_iscsi(self, targets, multipath):
        """Return iSCSI-specific server-LDEV connection info."""
        if not multipath:
            target_list = targets['list'][:1]
        else:
            target_list = targets['list'][:]

        for target in target_list:
            if target not in targets['iqns']:
                port, gid = target
                target_info = self.client.get_host_grp(port, gid)
                iqn = target_info.get('iscsiName') if target_info else None
                if not iqn:
                    msg = utils.output_log(MSG.RESOURCE_NOT_FOUND,
                                           resource='Target IQN')
                    self.raise_error(msg)
                targets['iqns'][target] = iqn
                LOG.debug(
                    'Found target iqn of host group. (port: %(port)s, '
                    'gid: %(gid)s, target iqn: %(iqn)s)',
                    {'port': port, 'gid': gid, 'iqn': iqn})
        return super(HBSDRESTISCSI, self).get_properties_iscsi(
            targets, multipath)

    def _get_iqn(self, port, hostgroup):
        """Get IQN from a port and the ISCSI target."""
        hba_iscsis = self.client.get_hba_iscsis_by_name(port, hostgroup)
        return hba_iscsis[0]['iscsiName']

    def set_terminate_target(self, fake_connector, port_hostgroup_map):
        """Set necessary information in connector in terminate."""
        for port, hostgroups in port_hostgroup_map.items():
            for hostgroup in hostgroups:
                iqn = self._get_iqn(port, hostgroup)
                if iqn:
                    fake_connector['initiator'] = iqn
                    return