Skip to content

Commit deef60a

Browse files
Merge pull request #16 from dell/Rv1.10.0
Rv1.10.0
2 parents 3ffddc9 + 793e06c commit deef60a

15 files changed

Lines changed: 324 additions & 40 deletions

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.10.0 - released on 29/03/24
4+
- Added support for retrieving all the firmware repository, validating, deploying, editing, adding nodes and deleting a resource group from PowerFlex Manager.
5+
36
## Version 1.9.0 - released on 29/02/24
47
- Added support for retrieving managed devices, service templates and deployments from PowerFlex Manager.
58

PyPowerFlex/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class PowerFlexClient:
4747
'replication_pair',
4848
'service_template',
4949
'managed_device',
50-
'deployment'
50+
'deployment',
51+
'firmware_repository'
5152
)
5253

5354
def __init__(self,
@@ -98,6 +99,7 @@ def initialize(self):
9899
self.__add_storage_entity('service_template', objects.ServiceTemplate)
99100
self.__add_storage_entity('managed_device', objects.ManagedDevice)
100101
self.__add_storage_entity('deployment', objects.Deployment)
102+
self.__add_storage_entity('firmware_repository', objects.FirmwareRepository)
101103
utils.init_logger(self.configuration.log_level)
102104
if version.parse(self.system.api_version()) < version.Version('3.0'):
103105
raise exceptions.PowerFlexClientException(

PyPowerFlex/base_client.py

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727

2828
class Request:
2929
GET = "get"
30+
POST = "post"
31+
PUT = "put"
32+
DELETE = "delete"
3033

3134
def __init__(self, token, configuration):
3235
self.token = token
@@ -67,39 +70,39 @@ def get_auth_headers(self, request_type=None):
6770
return {'Authorization': 'Bearer {0}'.format(self.token.get()),
6871
'content-type': 'application/json'}
6972

70-
def send_get_request(self, url, **url_params):
71-
request_url = self.base_url + url.format(**url_params)
73+
def send_request(self, method, url, params=None, **url_params):
74+
params = params or {}
75+
request_url = f"{self.base_url}{url.format(**url_params)}"
7276
version = self.login()
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+
request_params = {
78+
'headers': self.get_auth_headers(method),
79+
'verify': self.verify_certificate,
80+
'timeout': self.configuration.timeout
81+
}
7782
if utils.is_version_3(version):
78-
request_params['auth'] = (self.configuration.username,
79-
self.token.get())
83+
request_params['auth'] = (self.configuration.username, self.token.get())
8084
request_params['headers'] = None
81-
r = requests.get(**request_params)
85+
86+
if method in [self.PUT, self.POST]:
87+
request_params['data'] = utils.prepare_params(params)
88+
response = requests.request(method, request_url, **request_params)
8289
self.logout(version)
83-
response = r.json()
84-
return r, response
90+
return response
91+
92+
def send_get_request(self, url, params=None, **url_params):
93+
response = self.send_request(self.GET, url, params, **url_params)
94+
return response, response.json()
8595

8696
def send_post_request(self, url, params=None, **url_params):
87-
if params is None:
88-
params = dict()
89-
version = self.login()
90-
request_url = self.base_url + url.format(**url_params)
91-
r = requests.post(request_url,
92-
auth=(
93-
self.configuration.username,
94-
self.token.get()
95-
),
96-
headers=self.headers,
97-
data=utils.prepare_params(params),
98-
verify=self.verify_certificate,
99-
timeout=self.configuration.timeout)
100-
response = r.json()
101-
self.logout(version)
102-
return r, response
97+
response = self.send_request(self.POST, url, params, **url_params)
98+
return response, response.json()
99+
100+
def send_put_request(self, url, params=None, **url_params):
101+
response = self.send_request(self.PUT, url, params, **url_params)
102+
return response, response.json()
103+
104+
def send_delete_request(self, url, params=None, **url_params):
105+
return self.send_request(self.DELETE, url, params, **url_params)
103106

104107
def send_mdm_cluster_post_request(self, url, params=None, **url_params):
105108
if params is None:
@@ -234,6 +237,7 @@ class EntityRequest(Request):
234237
service_template_url = '/V1/ServiceTemplate'
235238
managed_device_url = '/V1/ManagedDevice'
236239
deployment_url = '/V1/Deployment'
240+
firmware_repository_url = '/V1/FirmwareRepository'
237241
entity_name = None
238242

239243
@property

PyPowerFlex/objects/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from PyPowerFlex.objects.service_template import ServiceTemplate
3030
from PyPowerFlex.objects.managed_device import ManagedDevice
3131
from PyPowerFlex.objects.deployment import Deployment
32+
from PyPowerFlex.objects.firmware_repository import FirmwareRepository
3233

3334

3435
__all__ = [
@@ -48,4 +49,5 @@
4849
'ServiceTemplate',
4950
'ManagedDevice',
5051
'Deployment',
52+
'FirmwareRepository',
5153
]

PyPowerFlex/objects/deployment.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,93 @@ def get(self, filters=None, full=None, include_devices=None, include_template=No
5050
LOG.error(msg)
5151
raise exceptions.PowerFlexClientException(msg)
5252
return response
53+
54+
def get_by_id(self, deployment_id):
55+
"""
56+
Retrieve Deployment for specified ID.
57+
:param deployment_id: Deployment ID.
58+
:return: A dictionary containing the retrieved Deployment.
59+
"""
60+
r, response = self.send_get_request(f'{self.deployment_url}/{deployment_id}')
61+
if r.status_code != requests.codes.ok:
62+
msg = (f'Failed to retrieve deployment by id {deployment_id}. Error: {response}')
63+
LOG.error(msg)
64+
raise exceptions.PowerFlexClientException(msg)
65+
return response
66+
67+
def validate(self, rg_data):
68+
"""
69+
Validates a new deployment.
70+
Args:
71+
rg_data (dict): The resource group data to be deployed.
72+
Returns:
73+
dict: The response from the deployment API.
74+
Raises:
75+
PowerFlexClientException: If the deployment fails.
76+
"""
77+
r, response = self.send_post_request(f'{self.deployment_url}/validate', rg_data)
78+
if r.status_code != requests.codes.ok:
79+
msg = (f'Failed to validate the deployment. Error: {response}')
80+
LOG.error(msg)
81+
raise exceptions.PowerFlexClientException(msg)
82+
83+
return response
84+
85+
def create(self, rg_data):
86+
"""
87+
Creates a new deployment.
88+
Args:
89+
rg_data (dict): The resource group data to be deployed.
90+
Returns:
91+
dict: The response from the deployment API.
92+
Raises:
93+
PowerFlexClientException: If the deployment fails.
94+
"""
95+
r, response = self.send_post_request(self.deployment_url, rg_data)
96+
if r.status_code != requests.codes.ok:
97+
msg = (f'Failed to create a new deployment. Error: {response}')
98+
LOG.error(msg)
99+
raise exceptions.PowerFlexClientException(msg)
100+
101+
return response
102+
103+
def edit(self, deployment_id, rg_data):
104+
"""
105+
Edit a deployment with the given ID using the provided data.
106+
Args:
107+
deployment_id (str): The ID of the deployment to edit.
108+
rg_data (dict): The data to use for editing the deployment.
109+
Returns:
110+
dict: The response from the API.
111+
Raises:
112+
PowerFlexClientException: If the request fails.
113+
"""
114+
request_url = f'{self.deployment_url}/{deployment_id}'
115+
r, response = self.send_put_request(request_url, rg_data)
116+
117+
if r.status_code != requests.codes.ok:
118+
msg = (f'Failed to edit the deployment. Error: {response}')
119+
LOG.error(msg)
120+
raise exceptions.PowerFlexClientException(msg)
121+
122+
return response
123+
124+
def delete(self, deployment_id):
125+
"""
126+
Deletes a deployment with the given ID.
127+
Args:
128+
deployment_id (str): The ID of the deployment to delete.
129+
Returns:
130+
str: The response from the delete request.
131+
Raises:
132+
exceptions.PowerFlexClientException: If the delete request fails.
133+
"""
134+
request_url = f'{self.deployment_url}/{deployment_id}'
135+
response = self.send_delete_request(request_url)
136+
137+
if response.status_code != requests.codes.no_content:
138+
msg = (f'Failed to delete deployment. Error: {response}')
139+
LOG.error(msg)
140+
raise exceptions.PowerFlexClientException(msg)
141+
142+
return response
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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 FirmwareRepository(base_client.EntityRequest):
25+
def get(self, filters=None, limit=None, offset=None, sort=None, related=False, bundles=False, components=False):
26+
"""
27+
Retrieve all firmware repository with filter, sort, pagination
28+
:param filters: (Optional) The filters to apply to the results.
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 related: Whether to include related entities in the response.
33+
:param bundles: Whether to include bundles in the response.
34+
:param components: Whether to include components in the response.
35+
:return: A list of dictionary containing the retrieved firmware repository.
36+
"""
37+
params = dict(
38+
filter=filters,
39+
sort=sort,
40+
offset=offset,
41+
limit=limit,
42+
related=related,
43+
bundles=bundles,
44+
components=components
45+
)
46+
r, response = self.send_get_request(utils.build_uri_with_params(self.firmware_repository_url, **params))
47+
if r.status_code != requests.codes.ok:
48+
msg = (f'Failed to retrieve firmware repository. Error: {response}')
49+
LOG.error(msg)
50+
raise exceptions.PowerFlexClientException(msg)
51+
return response

PyPowerFlex/objects/service_template.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,20 @@ def get(self, filters=None, full=None, limit=None, offset=None, sort=None, inclu
4646
LOG.error(msg)
4747
raise exceptions.PowerFlexClientException(msg)
4848
return response
49+
50+
def get_by_id(self, service_template_id, for_deployment=False):
51+
"""
52+
Retrieve a Service Template by its ID.
53+
:param service_template_id: The ID of the Service Template to retrieve.
54+
:param for_deployment: (Optional) Whether to retrieve the Service Template for deployment.
55+
:return: A dictionary containing the retrieved Service Template.
56+
"""
57+
url = f'{self.service_template_url}/{service_template_id}'
58+
if for_deployment:
59+
url += '?forDeployment=true'
60+
r, response = self.send_get_request(url)
61+
if r.status_code != requests.codes.ok:
62+
msg = (f'Failed to retrieve service template by id {service_template_id}. Error: {response}')
63+
LOG.error(msg)
64+
raise exceptions.PowerFlexClientException(msg)
65+
return response

PyPowerFlex/utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ def prepare_params(params, dump=True):
120120
:return: prepared parameters
121121
"""
122122

123+
if not isinstance(params, dict):
124+
return params
125+
123126
prepared = dict()
124127
for name, value in params.items():
125128
if value is not None:

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ python setup.py install
4444
* ManagedDevice
4545
* Deployment
4646
* ServiceTemplate
47+
* FirmwareRepository
4748

4849
#### Initialize PowerFlex client
4950

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
setup(
1919
name='PyPowerFlex',
20-
version='1.9.0',
20+
version='1.10.0',
2121
description='Python library for Dell PowerFlex',
2222
author='Ansible Team at Dell',
2323
author_email='ansible.team@dell.com',

0 commit comments

Comments
 (0)