python-3parclient 4.2.14__tar.gz → 4.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_3parclient-4.2.14 → python_3parclient-4.3}/PKG-INFO +3 -3
- python_3parclient-4.3/note.txt +7 -0
- {python_3parclient-4.2.14 → python_3parclient-4.3}/pyproject.toml +2 -2
- {python_3parclient-4.2.14 → python_3parclient-4.3}/src/hpe3parclient/client.py +320 -21
- {python_3parclient-4.2.14 → python_3parclient-4.3}/src/hpe3parclient/http.py +39 -8
- python_3parclient-4.2.14/note.txt +0 -2
- python_3parclient-4.2.14/src/hpe3parclient/.client.py.swp +0 -0
- {python_3parclient-4.2.14 → python_3parclient-4.3}/LICENSE +0 -0
- {python_3parclient-4.2.14 → python_3parclient-4.3}/README.md +0 -0
- {python_3parclient-4.2.14 → python_3parclient-4.3}/src/hpe3parclient/__init__.py +0 -0
- {python_3parclient-4.2.14 → python_3parclient-4.3}/src/hpe3parclient/exceptions.py +0 -0
- {python_3parclient-4.2.14 → python_3parclient-4.3}/src/hpe3parclient/file_client.py +0 -0
- {python_3parclient-4.2.14 → python_3parclient-4.3}/src/hpe3parclient/showport_parser.py +0 -0
- {python_3parclient-4.2.14 → python_3parclient-4.3}/src/hpe3parclient/ssh.py +0 -0
- {python_3parclient-4.2.14 → python_3parclient-4.3}/src/hpe3parclient/tcl_parser.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-3parclient
|
|
3
|
-
Version: 4.
|
|
4
|
-
Summary: HPE Alletra 9000
|
|
3
|
+
Version: 4.3
|
|
4
|
+
Summary: HPE Alletra MP, 9000, Primera and 3PAR REST Client
|
|
5
5
|
Author-email: Raghavendra Tilay <raghavendra-uddhav.tilay@hpe.com>
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -8,11 +8,11 @@ packages = ["src/hpe3parclient"]
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "python-3parclient"
|
|
11
|
-
version = "4.
|
|
11
|
+
version = "4.3"
|
|
12
12
|
authors = [
|
|
13
13
|
{ name="Raghavendra Tilay", email="raghavendra-uddhav.tilay@hpe.com" },
|
|
14
14
|
]
|
|
15
|
-
description = "HPE Alletra 9000
|
|
15
|
+
description = "HPE Alletra MP, 9000, Primera and 3PAR REST Client"
|
|
16
16
|
readme = "README.md"
|
|
17
17
|
classifiers = [
|
|
18
18
|
"Development Status :: 5 - Production/Stable",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# (c) Copyright 2012-
|
|
1
|
+
# (c) Copyright 2012-2025, 2026 Hewlett Packard Enterprise Development LP
|
|
2
2
|
# All Rights Reserved.
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
@@ -202,6 +202,9 @@ class HPE3ParClient(object):
|
|
|
202
202
|
RC_ACTION_CHANGE_TO_NATURUAL_DIRECTION = 10
|
|
203
203
|
RC_ACTION_OVERRIDE_FAIL_SAFE = 11
|
|
204
204
|
|
|
205
|
+
DEFAULT_NVME_PORT = 4420
|
|
206
|
+
DEFAULT_PORT_NQN = 'nqn.2014-08.org.nvmexpress.discovery'
|
|
207
|
+
|
|
205
208
|
def __init__(self, api_url, debug=False, secure=False, timeout=None,
|
|
206
209
|
suppress_ssl_warnings=False):
|
|
207
210
|
self.api_url = api_url
|
|
@@ -218,6 +221,9 @@ class HPE3ParClient(object):
|
|
|
218
221
|
|
|
219
222
|
def is_primera_array(self):
|
|
220
223
|
return self.primera_supported
|
|
224
|
+
|
|
225
|
+
def get_session_key(self):
|
|
226
|
+
return self.http.get_session_key()
|
|
221
227
|
|
|
222
228
|
def setSSHOptions(self, ip, login, password, port=22,
|
|
223
229
|
conn_timeout=None, privatekey=None,
|
|
@@ -250,22 +256,17 @@ class HPE3ParClient(object):
|
|
|
250
256
|
:returns: Version dict
|
|
251
257
|
|
|
252
258
|
"""
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
# get the api version
|
|
258
|
-
response, body = self.http.get('/api')
|
|
259
|
+
# remove everything down to host:port
|
|
260
|
+
host_url = self.api_url.split('/api')
|
|
261
|
+
# Build absolute URL and call without mutating http client's base
|
|
262
|
+
response, body = self.http.get(host_url[0] + '/api')
|
|
259
263
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
api_version = body
|
|
265
|
+
if (api_version['build'] >=
|
|
266
|
+
self.HPE3PAR_WS_PRIMERA_MIN_BUILD_VERSION):
|
|
267
|
+
self.primera_supported = True
|
|
264
268
|
|
|
265
|
-
|
|
266
|
-
finally:
|
|
267
|
-
# reset the url
|
|
268
|
-
self.http.set_url(self.api_url)
|
|
269
|
+
return body
|
|
269
270
|
|
|
270
271
|
def debug_rest(self, flag):
|
|
271
272
|
"""This is useful for debugging requests to 3PAR.
|
|
@@ -1610,6 +1611,35 @@ class HPE3ParClient(object):
|
|
|
1610
1611
|
response, body = self.http.get('/hosts/%s' % name)
|
|
1611
1612
|
return body
|
|
1612
1613
|
|
|
1614
|
+
def getHostByNqn(self, nqn):
|
|
1615
|
+
"""Get information about a Host by its NVMe Qualified Name (NQN).
|
|
1616
|
+
|
|
1617
|
+
:param nqn: The NQN of the Host to find
|
|
1618
|
+
:type nqn: str
|
|
1619
|
+
|
|
1620
|
+
:returns: host dict
|
|
1621
|
+
:raises: :class:`~hpe3parclient.exceptions.HTTPNotFound`
|
|
1622
|
+
- NON_EXISTENT_HOST - HOST doesn't exist
|
|
1623
|
+
|
|
1624
|
+
"""
|
|
1625
|
+
body = self.getHosts()
|
|
1626
|
+
if 'members' not in body:
|
|
1627
|
+
return None
|
|
1628
|
+
|
|
1629
|
+
for host in body['members']:
|
|
1630
|
+
if 'NVMETCPPaths' not in host or not host['NVMETCPPaths']:
|
|
1631
|
+
continue
|
|
1632
|
+
nvme_paths = host['NVMETCPPaths']
|
|
1633
|
+
for path in nvme_paths:
|
|
1634
|
+
path_nqn = path.get('NQN')
|
|
1635
|
+
if path_nqn == nqn:
|
|
1636
|
+
return host
|
|
1637
|
+
|
|
1638
|
+
# If we reach here, no host with the given NQN was found
|
|
1639
|
+
logger.error("Host with NQN '%s' not found.", nqn)
|
|
1640
|
+
msg = "Host with NQN '%s' not found." % nqn
|
|
1641
|
+
raise exceptions.HTTPNotFound(error={'desc': msg})
|
|
1642
|
+
|
|
1613
1643
|
def createHost(self, name, iscsiNames=None, FCWwns=None,
|
|
1614
1644
|
nqn=None, optional=None):
|
|
1615
1645
|
"""Create a new Host entry.
|
|
@@ -1694,8 +1724,19 @@ class HPE3ParClient(object):
|
|
|
1694
1724
|
if optional:
|
|
1695
1725
|
info = self._mergeDict(info, optional)
|
|
1696
1726
|
|
|
1697
|
-
|
|
1698
|
-
|
|
1727
|
+
try:
|
|
1728
|
+
response, body = self.http.post('/hosts', body=info)
|
|
1729
|
+
except Exception as ex:
|
|
1730
|
+
logger.error("Failed to create host: %s", str(ex))
|
|
1731
|
+
raise
|
|
1732
|
+
if response is not None and 'location' in response:
|
|
1733
|
+
location = response['location']
|
|
1734
|
+
logger.debug("Created host at: %s", location)
|
|
1735
|
+
hostObj = self.getHost(name)
|
|
1736
|
+
if hostObj is None:
|
|
1737
|
+
msg = "Host %s was created but not found." % name
|
|
1738
|
+
raise exceptions.HTTPNotFound(error={'desc': msg})
|
|
1739
|
+
return hostObj
|
|
1699
1740
|
|
|
1700
1741
|
def modifyHost(self, name, mod_request):
|
|
1701
1742
|
"""Modify an existing Host entry.
|
|
@@ -2192,7 +2233,7 @@ class HPE3ParClient(object):
|
|
|
2192
2233
|
response, body = self.http.get('/vluns')
|
|
2193
2234
|
return body
|
|
2194
2235
|
|
|
2195
|
-
def getVLUN(self, volumeName):
|
|
2236
|
+
def getVLUN(self, volumeName, allVluns=False):
|
|
2196
2237
|
"""Get information about a VLUN.
|
|
2197
2238
|
|
|
2198
2239
|
:param volumeName: The volume name of the VLUN to find
|
|
@@ -2211,9 +2252,14 @@ class HPE3ParClient(object):
|
|
|
2211
2252
|
response, body = self.http.get('/vluns?query=%s' %
|
|
2212
2253
|
quote(query.encode("utf8")))
|
|
2213
2254
|
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2255
|
+
if allVluns:
|
|
2256
|
+
# Return all the VLUNs found for the volume.
|
|
2257
|
+
vluns = body.get('members', [])
|
|
2258
|
+
return vluns
|
|
2259
|
+
else:
|
|
2260
|
+
# Return the first VLUN found for the volume.
|
|
2261
|
+
for vlun in body.get('members', []):
|
|
2262
|
+
return vlun
|
|
2217
2263
|
else:
|
|
2218
2264
|
vluns = self.getVLUNs()
|
|
2219
2265
|
if vluns:
|
|
@@ -5073,3 +5119,256 @@ class HPE3ParClient(object):
|
|
|
5073
5119
|
# it means task cannot be cancelled,
|
|
5074
5120
|
# because it is 'done' or already 'cancelled'
|
|
5075
5121
|
pass
|
|
5122
|
+
|
|
5123
|
+
def getNvmePorts(self):
|
|
5124
|
+
all_ports = self.getPorts()
|
|
5125
|
+
|
|
5126
|
+
target_ports = []
|
|
5127
|
+
for port in all_ports['members']:
|
|
5128
|
+
if (
|
|
5129
|
+
port['mode'] == self.PORT_MODE_TARGET and
|
|
5130
|
+
port['linkState'] == self.PORT_STATE_READY
|
|
5131
|
+
):
|
|
5132
|
+
port_pos = port['portPos']
|
|
5133
|
+
nsp = '%s:%s:%s' % (port_pos['node'], port_pos['slot'],
|
|
5134
|
+
port_pos['cardPort'])
|
|
5135
|
+
port['nsp'] = nsp
|
|
5136
|
+
target_ports.append(port)
|
|
5137
|
+
|
|
5138
|
+
nvme_ports = []
|
|
5139
|
+
for port in target_ports:
|
|
5140
|
+
if port['protocol'] == self.PORT_PROTO_NVME:
|
|
5141
|
+
nvme_ports.append(port)
|
|
5142
|
+
|
|
5143
|
+
logger.debug("nvme_ports: %(ports)s", {'ports': nvme_ports})
|
|
5144
|
+
return nvme_ports
|
|
5145
|
+
|
|
5146
|
+
def get_matched_array_ips_and_ports(self, client_conf):
|
|
5147
|
+
temp_nvme_ip = {}
|
|
5148
|
+
nvme_ip_list = {}
|
|
5149
|
+
conf_ips = client_conf['hpe3par_nvme_ips']
|
|
5150
|
+
|
|
5151
|
+
for ip_addr in conf_ips:
|
|
5152
|
+
# "ip"(given by user in cinder conf)
|
|
5153
|
+
# contains IP Address, NVMe port in <IP>:<PORT> format.
|
|
5154
|
+
ip = ip_addr.split(':')
|
|
5155
|
+
if len(ip) == 1:
|
|
5156
|
+
# "ip" doesn't contain NVMe port, use default NVMe port.
|
|
5157
|
+
temp_nvme_ip[ip_addr] = {'ip_port': self.DEFAULT_NVME_PORT}
|
|
5158
|
+
elif len(ip) == 2:
|
|
5159
|
+
#Valid format <IP>:<PORT>
|
|
5160
|
+
temp_nvme_ip[ip[0]] = {'ip_port': ip[1]}
|
|
5161
|
+
else:
|
|
5162
|
+
#Invalid format such as <IP>:<PORT>:<DATA>
|
|
5163
|
+
logger.warning("Invalid IP address format '%s'", ip_addr)
|
|
5164
|
+
|
|
5165
|
+
# get all the valid nvme ports from array
|
|
5166
|
+
# when found, add the valid nvme ip and port
|
|
5167
|
+
# to the nvme IP dictionary
|
|
5168
|
+
nvme_ports = self.getNvmePorts()
|
|
5169
|
+
if not len(nvme_ports):
|
|
5170
|
+
msg = 'Unable to obtain NVMe ports from storage system.'
|
|
5171
|
+
logger.error(msg)
|
|
5172
|
+
raise exceptions.HTTPNotFound(
|
|
5173
|
+
{'code': 'NON_EXISTENT_NVME_PORTS',
|
|
5174
|
+
'desc': msg})
|
|
5175
|
+
for port in nvme_ports:
|
|
5176
|
+
ip = port['nodeWWN']
|
|
5177
|
+
if ip in temp_nvme_ip:
|
|
5178
|
+
ip_port = temp_nvme_ip[ip]['ip_port']
|
|
5179
|
+
nvme_ip_list[ip] = {'ip_port': ip_port,
|
|
5180
|
+
'nsp': port['nsp']}
|
|
5181
|
+
del temp_nvme_ip[ip]
|
|
5182
|
+
|
|
5183
|
+
logger.debug("After mapping IPs from cinder to storage system:"
|
|
5184
|
+
"temp_nvme_ip: %(ips)s", {'ips': temp_nvme_ip})
|
|
5185
|
+
logger.debug("After mapping IPs from cinder to storage system:"
|
|
5186
|
+
"nvme_ip_list: %(ips)s", {'ips': nvme_ip_list})
|
|
5187
|
+
|
|
5188
|
+
# lets see if there are invalid nvme IPs left in the temp dict
|
|
5189
|
+
if len(temp_nvme_ip) > 0:
|
|
5190
|
+
logger.warning("Found invalid nvme IP address(s) in "
|
|
5191
|
+
"configuration option(s) hpe3par_nvme_ips '%s.'",
|
|
5192
|
+
(", ".join(temp_nvme_ip)))
|
|
5193
|
+
|
|
5194
|
+
if not len(nvme_ip_list):
|
|
5195
|
+
msg = 'At least one valid nvme IP address must be set.'
|
|
5196
|
+
logger.error(msg)
|
|
5197
|
+
raise exceptions.HTTPNotFound(
|
|
5198
|
+
{'code': 'NON_EXISTENT_NVME_IP',
|
|
5199
|
+
'desc': msg})
|
|
5200
|
+
|
|
5201
|
+
ret_vals = (nvme_ip_list, nvme_ports)
|
|
5202
|
+
return ret_vals
|
|
5203
|
+
|
|
5204
|
+
def create_host_or_use_existing(self, hostname, iscsiNames=None,
|
|
5205
|
+
FCWwns=None, nqn=None, domain=None):
|
|
5206
|
+
try:
|
|
5207
|
+
host = self.getHost(hostname)
|
|
5208
|
+
logger.debug("Host is already present:%(name)s",
|
|
5209
|
+
{'name': hostname})
|
|
5210
|
+
return host
|
|
5211
|
+
except exceptions.HTTPNotFound:
|
|
5212
|
+
logger.debug("Host doesn't exist. Creating host with %(name)s",
|
|
5213
|
+
{'name': hostname})
|
|
5214
|
+
try:
|
|
5215
|
+
host = self.createHost(hostname, iscsiNames=iscsiNames,
|
|
5216
|
+
FCWwns=FCWwns, nqn=nqn,
|
|
5217
|
+
optional={'domain': domain})
|
|
5218
|
+
except Exception as ex:
|
|
5219
|
+
logger.error("Exception occurred while creating host %(name)s: %(ex)s",
|
|
5220
|
+
{'name': hostname, 'ex': str(ex)})
|
|
5221
|
+
raise
|
|
5222
|
+
return host
|
|
5223
|
+
except Exception as ex:
|
|
5224
|
+
logger.error("Exception occurred while creating host %(name)s: %(ex)s",
|
|
5225
|
+
{'name': hostname, 'ex': str(ex)})
|
|
5226
|
+
raise
|
|
5227
|
+
|
|
5228
|
+
def create_host_nvme(self, hostname, nqn=None, domain=None):
|
|
5229
|
+
host = self.create_host_or_use_existing(hostname, nqn=nqn, domain=domain)
|
|
5230
|
+
return host
|
|
5231
|
+
|
|
5232
|
+
def getNextLunId(self, hostname, host_type='nvme'):
|
|
5233
|
+
# lun id can be 0 through 16383 (1 to 256 for NVMe hosts)
|
|
5234
|
+
LIMIT = 16383
|
|
5235
|
+
if host_type=='nvme':
|
|
5236
|
+
LIMIT = 256
|
|
5237
|
+
|
|
5238
|
+
lun_id_max = 0
|
|
5239
|
+
try:
|
|
5240
|
+
host_vluns = self.getHostVLUNs(hostname)
|
|
5241
|
+
for vlun in host_vluns:
|
|
5242
|
+
lun_id_x = vlun['lun']
|
|
5243
|
+
if lun_id_x > lun_id_max:
|
|
5244
|
+
lun_id_max = lun_id_x
|
|
5245
|
+
|
|
5246
|
+
except exceptions.HTTPNotFound:
|
|
5247
|
+
# ignore, no existing VLUNs were found
|
|
5248
|
+
pass
|
|
5249
|
+
|
|
5250
|
+
lun_id_next = lun_id_max + 1
|
|
5251
|
+
if lun_id_next > LIMIT:
|
|
5252
|
+
msg = "Lun id exceeded limit '%d'" % LIMIT
|
|
5253
|
+
raise exceptions.HTTPNotFound(error={'desc': msg})
|
|
5254
|
+
|
|
5255
|
+
return lun_id_next
|
|
5256
|
+
|
|
5257
|
+
def getNqn(self, portPos=None):
|
|
5258
|
+
# in dev and QA array, all ports have same nqn below:
|
|
5259
|
+
# 'nqn.2014-08.org.nvmexpress.discovery'
|
|
5260
|
+
return self.DEFAULT_PORT_NQN
|
|
5261
|
+
|
|
5262
|
+
def build_portPos(self, nsp):
|
|
5263
|
+
arr = nsp.split(":")
|
|
5264
|
+
portPos = {}
|
|
5265
|
+
portPos['node'] = int(arr[0])
|
|
5266
|
+
portPos['slot'] = int(arr[1])
|
|
5267
|
+
portPos['cardPort'] = int(arr[2])
|
|
5268
|
+
return portPos
|
|
5269
|
+
|
|
5270
|
+
def find_existing_vluns(self, vol_name_3par, host):
|
|
5271
|
+
existing_vluns = []
|
|
5272
|
+
try:
|
|
5273
|
+
host_vluns = self.getHostVLUNs(host['name'])
|
|
5274
|
+
for vlun in host_vluns:
|
|
5275
|
+
if vlun['volumeName'] == vol_name_3par:
|
|
5276
|
+
existing_vluns.append(vlun)
|
|
5277
|
+
except exceptions.HTTPNotFound:
|
|
5278
|
+
# ignore, no existing VLUNs were found
|
|
5279
|
+
logger.debug("No existing VLUNs were found for host/volume "
|
|
5280
|
+
"combination: %(host)s, %(vol)s",
|
|
5281
|
+
{'host': host['name'],
|
|
5282
|
+
'vol': vol_name_3par})
|
|
5283
|
+
return existing_vluns
|
|
5284
|
+
|
|
5285
|
+
def create_vlun_nvme(self, vol_name_3par, host, nvme_ips):
|
|
5286
|
+
"""Create a VLUN for NVMe host.
|
|
5287
|
+
:param vol_name_3par: The name of the volume on 3PAR.
|
|
5288
|
+
:param host: The host object containing host information.
|
|
5289
|
+
:param nvme_ips: The NVMe IPs to use for the VLUN.
|
|
5290
|
+
:returns: A tuple containing a list of portals and target NQNs.
|
|
5291
|
+
:rtype: tuple
|
|
5292
|
+
"""
|
|
5293
|
+
|
|
5294
|
+
# Collect all existing VLUNs for this volume/host combination.
|
|
5295
|
+
existing_vluns = self.find_existing_vluns(vol_name_3par, host)
|
|
5296
|
+
logger.debug("existing_vluns: %(ev)s", {'ev': existing_vluns})
|
|
5297
|
+
host_name = host['name']
|
|
5298
|
+
portals = []
|
|
5299
|
+
target_nqns = []
|
|
5300
|
+
lun_id = None
|
|
5301
|
+
# check for an already existing VLUN matching the
|
|
5302
|
+
# nsp for this nvme IP. If one is found, use it
|
|
5303
|
+
# instead of creating a new VLUN.
|
|
5304
|
+
if existing_vluns:
|
|
5305
|
+
for v in existing_vluns:
|
|
5306
|
+
lun_id = v['lun']
|
|
5307
|
+
logger.debug("vlun exists for host name: %(host)s" \
|
|
5308
|
+
" with lun: %(lun)s",
|
|
5309
|
+
{'host': host_name, 'lun': v['lun']})
|
|
5310
|
+
break
|
|
5311
|
+
else:
|
|
5312
|
+
logger.debug("creating vlun for host name: %(host)s",
|
|
5313
|
+
{'host': host_name})
|
|
5314
|
+
if lun_id is None:
|
|
5315
|
+
logger.debug("lun_id is None. calling getNextLunId")
|
|
5316
|
+
lun_id = self.getNextLunId(host_name)
|
|
5317
|
+
logger.debug("lun_id_next: %(id)s", {'id': lun_id})
|
|
5318
|
+
logger.debug("lun_id is %(id)s", {'id': lun_id})
|
|
5319
|
+
location = self.createVLUN(vol_name_3par, lun=lun_id,
|
|
5320
|
+
hostname=host_name)
|
|
5321
|
+
logger.debug("location: %(loc)s", {'loc': location})
|
|
5322
|
+
target_portal_ips = list(nvme_ips.keys())
|
|
5323
|
+
for nvme_ip in target_portal_ips:
|
|
5324
|
+
portals.append(
|
|
5325
|
+
(nvme_ip, nvme_ips[nvme_ip]['ip_port'], 'tcp')
|
|
5326
|
+
)
|
|
5327
|
+
#target_nqns.append(self.getNqn())
|
|
5328
|
+
vlun = self.getVLUN(vol_name_3par)
|
|
5329
|
+
nqn_of_vlun = vlun['Subsystem_NQN']
|
|
5330
|
+
logger.debug("nqn_of_vlun: %(nqn)s", {'nqn': nqn_of_vlun})
|
|
5331
|
+
target_nqns.append(nqn_of_vlun)
|
|
5332
|
+
|
|
5333
|
+
ret_vals = (portals, target_nqns)
|
|
5334
|
+
return ret_vals
|
|
5335
|
+
|
|
5336
|
+
def remove_vlun_nvme(self, vol_name_3par, hostname, host_nqn):
|
|
5337
|
+
vlunsData = self.getVLUN(vol_name_3par, True)
|
|
5338
|
+
print("vlunsData: ", vlunsData)
|
|
5339
|
+
if vlunsData == []:
|
|
5340
|
+
logger.error("No VLUN found for volume %(name)s on host %(host)s",
|
|
5341
|
+
{'name': vol_name_3par, 'host': hostname})
|
|
5342
|
+
return
|
|
5343
|
+
|
|
5344
|
+
# When deleteing VLUNs, you simply need to remove the template VLUN
|
|
5345
|
+
# and any active VLUNs will be automatically removed. The template
|
|
5346
|
+
# VLUN are marked as active: False
|
|
5347
|
+
vluns = []
|
|
5348
|
+
for vlun in vlunsData:
|
|
5349
|
+
if vol_name_3par in vlun['volumeName']:
|
|
5350
|
+
# template VLUNs are 'active' = False
|
|
5351
|
+
if not vlun['active']:
|
|
5352
|
+
vluns.append(vlun)
|
|
5353
|
+
|
|
5354
|
+
print("vluns: ", vluns)
|
|
5355
|
+
if not vluns:
|
|
5356
|
+
logger.warning("3PAR vlun for volume %(name)s not found on host "
|
|
5357
|
+
"%(host)s", {'name': vol_name_3par, 'host': hostname})
|
|
5358
|
+
return
|
|
5359
|
+
|
|
5360
|
+
for vlun in vluns:
|
|
5361
|
+
print(" -- vlun: ", vlun)
|
|
5362
|
+
# Check if this VLUN belongs to the specified hostname
|
|
5363
|
+
if vlun.get('hostname') == hostname:
|
|
5364
|
+
logger.debug("deleting vlun: %(lun)s", {'lun': vlun})
|
|
5365
|
+
print("deleting vlun")
|
|
5366
|
+
self.deleteVLUN(vol_name_3par, vlun['lun'],
|
|
5367
|
+
hostname)
|
|
5368
|
+
# port=vlun['portPos'])
|
|
5369
|
+
else:
|
|
5370
|
+
logger.debug("Skipping vlun: %(lun)s -"
|
|
5371
|
+
" belongs to different host: %(vlun_host)s",
|
|
5372
|
+
{'lun': vlun['lun'],
|
|
5373
|
+
'vlun_host': vlun.get('hostname', 'Unknown')})
|
|
5374
|
+
|
|
@@ -72,6 +72,8 @@ class HTTPJSONRESTClient(object):
|
|
|
72
72
|
if suppress_ssl_warnings:
|
|
73
73
|
requests.packages.urllib3.disable_warnings()
|
|
74
74
|
|
|
75
|
+
HTTPJSONRESTClient._logger.debug("Initializing Session Key in __init__:")
|
|
76
|
+
|
|
75
77
|
self.session_key = None
|
|
76
78
|
|
|
77
79
|
# should be http://<Server:Port>/api/v1
|
|
@@ -86,6 +88,9 @@ class HTTPJSONRESTClient(object):
|
|
|
86
88
|
# should be http://<Server:Port>/api/v1
|
|
87
89
|
self.api_url = api_url.rstrip('/')
|
|
88
90
|
|
|
91
|
+
def get_session_key(self):
|
|
92
|
+
return self.session_key
|
|
93
|
+
|
|
89
94
|
def set_debug_flag(self, flag):
|
|
90
95
|
"""
|
|
91
96
|
This turns on/off http request/response debugging output to console
|
|
@@ -111,6 +116,8 @@ class HTTPJSONRESTClient(object):
|
|
|
111
116
|
|
|
112
117
|
"""
|
|
113
118
|
# this prevens re-auth attempt if auth fails
|
|
119
|
+
HTTPJSONRESTClient._logger.debug("Session Key in authenticate: %s\n" %
|
|
120
|
+
(self.session_key))
|
|
114
121
|
self.auth_try = 1
|
|
115
122
|
self.session_key = None
|
|
116
123
|
|
|
@@ -136,9 +143,16 @@ class HTTPJSONRESTClient(object):
|
|
|
136
143
|
This clears the authenticated session with the 3PAR server.
|
|
137
144
|
|
|
138
145
|
"""
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
146
|
+
try:
|
|
147
|
+
# delete the session on the 3Par
|
|
148
|
+
HTTPJSONRESTClient._logger.debug("Session Key in unauthenticate: %s\n" %
|
|
149
|
+
(self.session_key))
|
|
150
|
+
self.delete('/credentials/%s' % self.session_key)
|
|
151
|
+
self.session_key = None
|
|
152
|
+
except Exception as ex:
|
|
153
|
+
HTTPJSONRESTClient._logger.error("Error during unauthenticate: %s\n"
|
|
154
|
+
% (str(ex)))
|
|
155
|
+
raise ex
|
|
142
156
|
|
|
143
157
|
def get_timings(self):
|
|
144
158
|
"""
|
|
@@ -275,7 +289,7 @@ class HTTPJSONRESTClient(object):
|
|
|
275
289
|
self.delay = self.delay * self.backoff + 1
|
|
276
290
|
|
|
277
291
|
# Raise exception, we have exhausted all retries.
|
|
278
|
-
if self.tries
|
|
292
|
+
if self.tries == 0:
|
|
279
293
|
raise ex
|
|
280
294
|
except requests.exceptions.HTTPError as err:
|
|
281
295
|
raise exceptions.HTTPError("HTTP Error: %s" % err)
|
|
@@ -292,8 +306,17 @@ class HTTPJSONRESTClient(object):
|
|
|
292
306
|
|
|
293
307
|
return resp, body
|
|
294
308
|
|
|
309
|
+
def _build_full_url(self, url):
|
|
310
|
+
"""Build full URL, supporting both absolute and relative URLs."""
|
|
311
|
+
if url.startswith('http://') or url.startswith('https://'):
|
|
312
|
+
return url.rstrip('/')
|
|
313
|
+
else:
|
|
314
|
+
return self.api_url + url
|
|
315
|
+
|
|
295
316
|
def _time_request(self, url, method, **kwargs):
|
|
296
317
|
start_time = time.time()
|
|
318
|
+
HTTPJSONRESTClient._logger.debug("url in _time_request: %s\n" %
|
|
319
|
+
(url))
|
|
297
320
|
resp, body = self.request(url, method, **kwargs)
|
|
298
321
|
self.times.append(("%s %s" % (method, url),
|
|
299
322
|
start_time, time.time()))
|
|
@@ -301,11 +324,15 @@ class HTTPJSONRESTClient(object):
|
|
|
301
324
|
|
|
302
325
|
def _do_reauth(self, url, method, ex, **kwargs):
|
|
303
326
|
# print("_do_reauth called")
|
|
327
|
+
HTTPJSONRESTClient._logger.debug("session key in _do_reauth: %s\n" %
|
|
328
|
+
(self.session_key))
|
|
329
|
+
HTTPJSONRESTClient._logger.debug("auth_try in _do_reauth: %s\n" %
|
|
330
|
+
(self.auth_try))
|
|
304
331
|
try:
|
|
305
332
|
if self.auth_try != 1:
|
|
306
333
|
self._reauth()
|
|
307
|
-
|
|
308
|
-
|
|
334
|
+
full_url = self._build_full_url(url)
|
|
335
|
+
resp, body = self._time_request(full_url, method, **kwargs)
|
|
309
336
|
return resp, body
|
|
310
337
|
else:
|
|
311
338
|
raise ex
|
|
@@ -317,8 +344,12 @@ class HTTPJSONRESTClient(object):
|
|
|
317
344
|
# might be because the auth token expired, so try to
|
|
318
345
|
# re-authenticate and try again. If it still fails, bail.
|
|
319
346
|
try:
|
|
320
|
-
|
|
321
|
-
|
|
347
|
+
HTTPJSONRESTClient._logger.debug("url in _cs_request: %s\n" %
|
|
348
|
+
(url))
|
|
349
|
+
full_url = self._build_full_url(url)
|
|
350
|
+
HTTPJSONRESTClient._logger.debug("full url in _cs_request: %s\n" %
|
|
351
|
+
(full_url))
|
|
352
|
+
resp, body = self._time_request(full_url, method, **kwargs)
|
|
322
353
|
return resp, body
|
|
323
354
|
except exceptions.HTTPUnauthorized as ex:
|
|
324
355
|
# print("_CS_REQUEST HTTPUnauthorized")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|