Skip to content

Commit 3ffddc9

Browse files
Merge pull request #14 from Bhavneet-Sharma/release_1.9.0
Release version 1.9.0
2 parents a262ceb + 3b6859a commit 3ffddc9

File tree

14 files changed

+346
-48
lines changed

14 files changed

+346
-48
lines changed

ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# PyPowerFlex Change Log
22

3+
## Version 1.9.0 - released on 29/02/24
4+
- Added support for retrieving managed devices, service templates and deployments from PowerFlex Manager.
5+
36
## Version 1.8.0 - released on 30/06/23
47
- Added block provisioning operations includes modifying performance profile in SDC, adding statistics data for snapshot policy, adding gateway configuration details for system, failover, restore, reverse, switchover, and sync operations in replication consistency group.
58

PyPowerFlex/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
1+
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
22
# All Rights Reserved.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -44,7 +44,10 @@ class PowerFlexClient:
4444
'volume',
4545
'utility',
4646
'replication_consistency_group',
47-
'replication_pair'
47+
'replication_pair',
48+
'service_template',
49+
'managed_device',
50+
'deployment'
4851
)
4952

5053
def __init__(self,
@@ -92,6 +95,9 @@ def initialize(self):
9295
self.__add_storage_entity('utility', objects.PowerFlexUtility)
9396
self.__add_storage_entity('replication_consistency_group', objects.ReplicationConsistencyGroup)
9497
self.__add_storage_entity('replication_pair', objects.ReplicationPair)
98+
self.__add_storage_entity('service_template', objects.ServiceTemplate)
99+
self.__add_storage_entity('managed_device', objects.ManagedDevice)
100+
self.__add_storage_entity('deployment', objects.Deployment)
95101
utils.init_logger(self.configuration.log_level)
96102
if version.parse(self.system.api_version()) < version.Version('3.0'):
97103
raise exceptions.PowerFlexClientException(

PyPowerFlex/base_client.py

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
1+
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
22
# All Rights Reserved.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -21,12 +21,13 @@
2121
from PyPowerFlex import exceptions
2222
from PyPowerFlex import utils
2323

24-
2524
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
2625
LOG = logging.getLogger(__name__)
2726

2827

2928
class Request:
29+
GET = "get"
30+
3031
def __init__(self, token, configuration):
3132
self.token = token
3233
self.configuration = configuration
@@ -60,17 +61,24 @@ def verify_certificate(self):
6061
verify_certificate = self.configuration.certificate_path
6162
return verify_certificate
6263

64+
def get_auth_headers(self, request_type=None):
65+
if request_type == self.GET:
66+
return {'Authorization': 'Bearer {0}'.format(self.token.get())}
67+
return {'Authorization': 'Bearer {0}'.format(self.token.get()),
68+
'content-type': 'application/json'}
69+
6370
def send_get_request(self, url, **url_params):
6471
request_url = self.base_url + url.format(**url_params)
6572
version = self.login()
66-
r = requests.get(request_url,
67-
auth=(
68-
self.configuration.username,
69-
self.token.get()
70-
),
71-
verify=self.verify_certificate,
72-
timeout=self.configuration.timeout)
73-
73+
request_params = {'url': request_url,
74+
'headers': self.get_auth_headers(request_type=self.GET),
75+
'verify': self.verify_certificate,
76+
'timeout': self.configuration.timeout}
77+
if utils.is_version_3(version):
78+
request_params['auth'] = (self.configuration.username,
79+
self.token.get())
80+
request_params['headers'] = None
81+
r = requests.get(**request_params)
7482
self.logout(version)
7583
response = r.json()
7684
return r, response
@@ -117,18 +125,18 @@ def send_mdm_cluster_post_request(self, url, params=None, **url_params):
117125
# To perform login based on the API version
118126
def login(self):
119127
version = self.get_api_version()
120-
if utils.check_version(version=version):
121-
self._appliance_login()
122-
else:
128+
if utils.is_version_3(version=version):
123129
self._login()
130+
else:
131+
self._appliance_login()
124132
return version
125133

126134
# To perform logout based on the API version
127135
def logout(self, version):
128-
if utils.check_version(version=version):
129-
self._appliance_logout()
130-
else:
136+
if utils.is_version_3(version=version):
131137
self._logout()
138+
else:
139+
self._appliance_logout()
132140

133141
# Get the Current API version
134142
def get_api_version(self):
@@ -165,12 +173,8 @@ def _appliance_login(self):
165173
# API logout method for 4.0 and above.
166174
def _appliance_logout(self):
167175
request_url = self.auth_url + '/logout'
168-
token = self.token.get()
169-
headers = {'Authorization': 'Bearer {0}'.format(token),
170-
'content-type': 'application/json'
171-
}
172176
data = {'refresh_token': '{0}'.format(self.__refresh_token)}
173-
r = requests.post(request_url, headers=headers, json=data,
177+
r = requests.post(request_url, headers=self.get_auth_headers(), json=data,
174178
verify=self.verify_certificate,
175179
timeout=self.configuration.timeout
176180
)
@@ -184,20 +188,21 @@ def _appliance_logout(self):
184188

185189
def _login(self):
186190
request_url = self.base_url + '/login'
187-
188-
r = requests.get(request_url,
189-
auth=(
190-
self.configuration.username,
191-
self.configuration.password
192-
),
193-
verify=self.verify_certificate,
194-
timeout=self.configuration.timeout)
195-
if r.status_code != requests.codes.ok:
196-
exc = exceptions.PowerFlexFailQuerying('token')
197-
LOG.error(exc.message)
198-
raise exc
199-
token = r.json()
200-
self.token.set(token)
191+
try:
192+
r = requests.get(request_url,
193+
auth=(
194+
self.configuration.username,
195+
self.configuration.password
196+
),
197+
verify=self.verify_certificate,
198+
timeout=self.configuration.timeout)
199+
r.raise_for_status()
200+
token = r.json()
201+
self.token.set(token)
202+
except requests.exceptions.RequestException as e:
203+
error_msg = f'Login failed with error:{e.response.content}' if e.response else f'Login failed with error:{str(e)}'
204+
LOG.error(error_msg)
205+
raise Exception(error_msg)
201206

202207
def _logout(self):
203208
token = self.token.get()
@@ -226,6 +231,9 @@ class EntityRequest(Request):
226231
base_object_url = '/instances/{entity}/action/{action}'
227232
query_mdm_cluster_url = '/instances/{entity}/queryMdmCluster'
228233
list_statistics_url = '/types/{entity}/instances/action/{action}'
234+
service_template_url = '/V1/ServiceTemplate'
235+
managed_device_url = '/V1/ManagedDevice'
236+
deployment_url = '/V1/Deployment'
229237
entity_name = None
230238

231239
@property

PyPowerFlex/objects/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
1+
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
22
# All Rights Reserved.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -26,6 +26,9 @@
2626
from PyPowerFlex.objects.utility import PowerFlexUtility
2727
from PyPowerFlex.objects.replication_consistency_group import ReplicationConsistencyGroup
2828
from PyPowerFlex.objects.replication_pair import ReplicationPair
29+
from PyPowerFlex.objects.service_template import ServiceTemplate
30+
from PyPowerFlex.objects.managed_device import ManagedDevice
31+
from PyPowerFlex.objects.deployment import Deployment
2932

3033

3134
__all__ = [
@@ -41,5 +44,8 @@
4144
'Volume',
4245
'PowerFlexUtility',
4346
'ReplicationConsistencyGroup',
44-
'ReplicationPair'
47+
'ReplicationPair',
48+
'ServiceTemplate',
49+
'ManagedDevice',
50+
'Deployment',
4551
]

PyPowerFlex/objects/deployment.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
import logging
17+
import requests
18+
from PyPowerFlex import base_client
19+
from PyPowerFlex import exceptions
20+
from PyPowerFlex import utils
21+
LOG = logging.getLogger(__name__)
22+
23+
24+
class Deployment(base_client.EntityRequest):
25+
def get(self, filters=None, full=None, include_devices=None, include_template=None,
26+
limit=None, offset=None, sort=None):
27+
"""
28+
Retrieve all Deployments with filter, sort, pagination
29+
:param filters: (Optional) The filters to apply to the results.
30+
:param full: (Optional) Whether to return full details for each result.
31+
:param include_devices: (Optional) Whether to include devices in the response.
32+
:param include_template: (Optional) Whether to include service templates in the response.
33+
:param limit: (Optional) Page limit.
34+
:param offset: (Optional) Pagination offset.
35+
:param sort: (Optional) The field to sort the results by.
36+
:return: A list of dictionary containing the retrieved Deployments.
37+
"""
38+
params = dict(
39+
filter=filters,
40+
full=full,
41+
sort=sort,
42+
offset=offset,
43+
limit=limit,
44+
includeDevices=include_devices,
45+
includeTemplate=include_template
46+
)
47+
r, response = self.send_get_request(utils.build_uri_with_params(self.deployment_url, **params))
48+
if r.status_code != requests.codes.ok:
49+
msg = (f'Failed to retrieve deployments. Error: {response}')
50+
LOG.error(msg)
51+
raise exceptions.PowerFlexClientException(msg)
52+
return response
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
import logging
17+
import requests
18+
from PyPowerFlex import base_client
19+
from PyPowerFlex import exceptions
20+
from PyPowerFlex import utils
21+
LOG = logging.getLogger(__name__)
22+
23+
class ManagedDevice(base_client.EntityRequest):
24+
def get(self, filters=None, limit=None, offset=None, sort=None):
25+
"""
26+
Retrieve all devices from inventory with filter, sort, pagination
27+
:param filters: (Optional) The filters to apply to the results.
28+
:param limit: (Optional) Page limit.
29+
:param offset: (Optional) Pagination offset.
30+
:param sort: (Optional) The field to sort the results by.
31+
:return: A list of dictionary containing the retrieved devices from inventory.
32+
"""
33+
params = dict(
34+
filter=filters,
35+
limit=limit,
36+
offset=offset,
37+
sort=sort
38+
)
39+
r, response = self.send_get_request(utils.build_uri_with_params(self.managed_device_url, **params))
40+
if r.status_code != requests.codes.ok:
41+
msg = (f'Failed to retrieve managed devices. Error: {response}')
42+
LOG.error(msg)
43+
raise exceptions.PowerFlexClientException(msg)
44+
return response
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
import logging
17+
import requests
18+
from PyPowerFlex import base_client
19+
from PyPowerFlex import exceptions
20+
from PyPowerFlex import utils
21+
LOG = logging.getLogger(__name__)
22+
23+
class ServiceTemplate(base_client.EntityRequest):
24+
def get(self, filters=None, full=None, limit=None, offset=None, sort=None, include_attachments=None):
25+
"""
26+
Retrieve all Service Templates with filter, sort, pagination
27+
:param filters: (Optional) The filters to apply to the results.
28+
:param full: (Optional) Whether to return full details for each result.
29+
:param limit: (Optional) Page limit.
30+
:param offset: (Optional) Pagination offset.
31+
:param sort: (Optional) The field to sort the results by.
32+
:param include_attachments: (Optional) Whether to include attachments.
33+
:return: A list of dictionary containing the retrieved Service Templates.
34+
"""
35+
params = dict(
36+
filter=filters,
37+
full=full,
38+
limit=limit,
39+
offset=offset,
40+
sort=sort,
41+
includeAttachments=include_attachments
42+
)
43+
r, response = self.send_get_request(utils.build_uri_with_params(self.service_template_url, **params))
44+
if r.status_code != requests.codes.ok:
45+
msg = (f'Failed to retrieve service templates. Error: {response}')
46+
LOG.error(msg)
47+
raise exceptions.PowerFlexClientException(msg)
48+
return response

PyPowerFlex/utils.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
1+
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
22
# All Rights Reserved.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -20,7 +20,6 @@
2020

2121
from PyPowerFlex import exceptions
2222

23-
2423
def init_logger(log_level):
2524
"""Initialize logger for PowerFlex client.
2625
@@ -130,14 +129,20 @@ def prepare_params(params, dump=True):
130129
return prepared
131130

132131

133-
def check_version(version):
132+
def is_version_3(version):
134133
""" Check the API version.
135134
136135
:param version: Specifies the current API version
137-
:return: True if API version is greater than or equal to 4.0
136+
:return: True if API version is lesser than 4.0
138137
:rtype: bool
139138
"""
140139
appliance_version = "4.0"
141-
if version >= appliance_version:
140+
if version < appliance_version:
142141
return True
143142
return False
143+
144+
def build_uri_with_params(uri, **url_params):
145+
query_params = [f"{key}={item}" if isinstance(value, list) else f"{key}={value}" for key, value in url_params.items() for item in (value if isinstance(value, list) else [value]) if item is not None]
146+
if query_params:
147+
uri += '?' + '&'.join(query_params)
148+
return uri

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ python setup.py install
4141
* Sdc
4242
* FaultSet
4343
* Volume
44+
* ManagedDevice
45+
* Deployment
46+
* ServiceTemplate
4447

4548
#### Initialize PowerFlex client
4649

0 commit comments

Comments
 (0)