Skip to content

Commit

Permalink
Merge pull request #14 from Bhavneet-Sharma/release_1.9.0
Browse files Browse the repository at this point in the history
Release version 1.9.0
  • Loading branch information
Bhavneet-Sharma authored Feb 27, 2024
2 parents a262ceb + 3b6859a commit 3ffddc9
Show file tree
Hide file tree
Showing 14 changed files with 346 additions and 48 deletions.
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# PyPowerFlex Change Log

## Version 1.9.0 - released on 29/02/24
- Added support for retrieving managed devices, service templates and deployments from PowerFlex Manager.

## Version 1.8.0 - released on 30/06/23
- 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.

Expand Down
10 changes: 8 additions & 2 deletions PyPowerFlex/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
Expand Down Expand Up @@ -44,7 +44,10 @@ class PowerFlexClient:
'volume',
'utility',
'replication_consistency_group',
'replication_pair'
'replication_pair',
'service_template',
'managed_device',
'deployment'
)

def __init__(self,
Expand Down Expand Up @@ -92,6 +95,9 @@ def initialize(self):
self.__add_storage_entity('utility', objects.PowerFlexUtility)
self.__add_storage_entity('replication_consistency_group', objects.ReplicationConsistencyGroup)
self.__add_storage_entity('replication_pair', objects.ReplicationPair)
self.__add_storage_entity('service_template', objects.ServiceTemplate)
self.__add_storage_entity('managed_device', objects.ManagedDevice)
self.__add_storage_entity('deployment', objects.Deployment)
utils.init_logger(self.configuration.log_level)
if version.parse(self.system.api_version()) < version.Version('3.0'):
raise exceptions.PowerFlexClientException(
Expand Down
78 changes: 43 additions & 35 deletions PyPowerFlex/base_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
Expand All @@ -21,12 +21,13 @@
from PyPowerFlex import exceptions
from PyPowerFlex import utils


requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
LOG = logging.getLogger(__name__)


class Request:
GET = "get"

def __init__(self, token, configuration):
self.token = token
self.configuration = configuration
Expand Down Expand Up @@ -60,17 +61,24 @@ def verify_certificate(self):
verify_certificate = self.configuration.certificate_path
return verify_certificate

def get_auth_headers(self, request_type=None):
if request_type == self.GET:
return {'Authorization': 'Bearer {0}'.format(self.token.get())}
return {'Authorization': 'Bearer {0}'.format(self.token.get()),
'content-type': 'application/json'}

def send_get_request(self, url, **url_params):
request_url = self.base_url + url.format(**url_params)
version = self.login()
r = requests.get(request_url,
auth=(
self.configuration.username,
self.token.get()
),
verify=self.verify_certificate,
timeout=self.configuration.timeout)

request_params = {'url': request_url,
'headers': self.get_auth_headers(request_type=self.GET),
'verify': self.verify_certificate,
'timeout': self.configuration.timeout}
if utils.is_version_3(version):
request_params['auth'] = (self.configuration.username,
self.token.get())
request_params['headers'] = None
r = requests.get(**request_params)
self.logout(version)
response = r.json()
return r, response
Expand Down Expand Up @@ -117,18 +125,18 @@ def send_mdm_cluster_post_request(self, url, params=None, **url_params):
# To perform login based on the API version
def login(self):
version = self.get_api_version()
if utils.check_version(version=version):
self._appliance_login()
else:
if utils.is_version_3(version=version):
self._login()
else:
self._appliance_login()
return version

# To perform logout based on the API version
def logout(self, version):
if utils.check_version(version=version):
self._appliance_logout()
else:
if utils.is_version_3(version=version):
self._logout()
else:
self._appliance_logout()

# Get the Current API version
def get_api_version(self):
Expand Down Expand Up @@ -165,12 +173,8 @@ def _appliance_login(self):
# API logout method for 4.0 and above.
def _appliance_logout(self):
request_url = self.auth_url + '/logout'
token = self.token.get()
headers = {'Authorization': 'Bearer {0}'.format(token),
'content-type': 'application/json'
}
data = {'refresh_token': '{0}'.format(self.__refresh_token)}
r = requests.post(request_url, headers=headers, json=data,
r = requests.post(request_url, headers=self.get_auth_headers(), json=data,
verify=self.verify_certificate,
timeout=self.configuration.timeout
)
Expand All @@ -184,20 +188,21 @@ def _appliance_logout(self):

def _login(self):
request_url = self.base_url + '/login'

r = requests.get(request_url,
auth=(
self.configuration.username,
self.configuration.password
),
verify=self.verify_certificate,
timeout=self.configuration.timeout)
if r.status_code != requests.codes.ok:
exc = exceptions.PowerFlexFailQuerying('token')
LOG.error(exc.message)
raise exc
token = r.json()
self.token.set(token)
try:
r = requests.get(request_url,
auth=(
self.configuration.username,
self.configuration.password
),
verify=self.verify_certificate,
timeout=self.configuration.timeout)
r.raise_for_status()
token = r.json()
self.token.set(token)
except requests.exceptions.RequestException as e:
error_msg = f'Login failed with error:{e.response.content}' if e.response else f'Login failed with error:{str(e)}'
LOG.error(error_msg)
raise Exception(error_msg)

def _logout(self):
token = self.token.get()
Expand Down Expand Up @@ -226,6 +231,9 @@ class EntityRequest(Request):
base_object_url = '/instances/{entity}/action/{action}'
query_mdm_cluster_url = '/instances/{entity}/queryMdmCluster'
list_statistics_url = '/types/{entity}/instances/action/{action}'
service_template_url = '/V1/ServiceTemplate'
managed_device_url = '/V1/ManagedDevice'
deployment_url = '/V1/Deployment'
entity_name = None

@property
Expand Down
10 changes: 8 additions & 2 deletions PyPowerFlex/objects/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
Expand Down Expand Up @@ -26,6 +26,9 @@
from PyPowerFlex.objects.utility import PowerFlexUtility
from PyPowerFlex.objects.replication_consistency_group import ReplicationConsistencyGroup
from PyPowerFlex.objects.replication_pair import ReplicationPair
from PyPowerFlex.objects.service_template import ServiceTemplate
from PyPowerFlex.objects.managed_device import ManagedDevice
from PyPowerFlex.objects.deployment import Deployment


__all__ = [
Expand All @@ -41,5 +44,8 @@
'Volume',
'PowerFlexUtility',
'ReplicationConsistencyGroup',
'ReplicationPair'
'ReplicationPair',
'ServiceTemplate',
'ManagedDevice',
'Deployment',
]
52 changes: 52 additions & 0 deletions PyPowerFlex/objects/deployment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# 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.

import logging
import requests
from PyPowerFlex import base_client
from PyPowerFlex import exceptions
from PyPowerFlex import utils
LOG = logging.getLogger(__name__)


class Deployment(base_client.EntityRequest):
def get(self, filters=None, full=None, include_devices=None, include_template=None,
limit=None, offset=None, sort=None):
"""
Retrieve all Deployments with filter, sort, pagination
:param filters: (Optional) The filters to apply to the results.
:param full: (Optional) Whether to return full details for each result.
:param include_devices: (Optional) Whether to include devices in the response.
:param include_template: (Optional) Whether to include service templates in the response.
:param limit: (Optional) Page limit.
:param offset: (Optional) Pagination offset.
:param sort: (Optional) The field to sort the results by.
:return: A list of dictionary containing the retrieved Deployments.
"""
params = dict(
filter=filters,
full=full,
sort=sort,
offset=offset,
limit=limit,
includeDevices=include_devices,
includeTemplate=include_template
)
r, response = self.send_get_request(utils.build_uri_with_params(self.deployment_url, **params))
if r.status_code != requests.codes.ok:
msg = (f'Failed to retrieve deployments. Error: {response}')
LOG.error(msg)
raise exceptions.PowerFlexClientException(msg)
return response
44 changes: 44 additions & 0 deletions PyPowerFlex/objects/managed_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# 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.

import logging
import requests
from PyPowerFlex import base_client
from PyPowerFlex import exceptions
from PyPowerFlex import utils
LOG = logging.getLogger(__name__)

class ManagedDevice(base_client.EntityRequest):
def get(self, filters=None, limit=None, offset=None, sort=None):
"""
Retrieve all devices from inventory with filter, sort, pagination
:param filters: (Optional) The filters to apply to the results.
:param limit: (Optional) Page limit.
:param offset: (Optional) Pagination offset.
:param sort: (Optional) The field to sort the results by.
:return: A list of dictionary containing the retrieved devices from inventory.
"""
params = dict(
filter=filters,
limit=limit,
offset=offset,
sort=sort
)
r, response = self.send_get_request(utils.build_uri_with_params(self.managed_device_url, **params))
if r.status_code != requests.codes.ok:
msg = (f'Failed to retrieve managed devices. Error: {response}')
LOG.error(msg)
raise exceptions.PowerFlexClientException(msg)
return response
48 changes: 48 additions & 0 deletions PyPowerFlex/objects/service_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# 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.

import logging
import requests
from PyPowerFlex import base_client
from PyPowerFlex import exceptions
from PyPowerFlex import utils
LOG = logging.getLogger(__name__)

class ServiceTemplate(base_client.EntityRequest):
def get(self, filters=None, full=None, limit=None, offset=None, sort=None, include_attachments=None):
"""
Retrieve all Service Templates with filter, sort, pagination
:param filters: (Optional) The filters to apply to the results.
:param full: (Optional) Whether to return full details for each result.
:param limit: (Optional) Page limit.
:param offset: (Optional) Pagination offset.
:param sort: (Optional) The field to sort the results by.
:param include_attachments: (Optional) Whether to include attachments.
:return: A list of dictionary containing the retrieved Service Templates.
"""
params = dict(
filter=filters,
full=full,
limit=limit,
offset=offset,
sort=sort,
includeAttachments=include_attachments
)
r, response = self.send_get_request(utils.build_uri_with_params(self.service_template_url, **params))
if r.status_code != requests.codes.ok:
msg = (f'Failed to retrieve service templates. Error: {response}')
LOG.error(msg)
raise exceptions.PowerFlexClientException(msg)
return response
15 changes: 10 additions & 5 deletions PyPowerFlex/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# Copyright (c) 2024 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
Expand All @@ -20,7 +20,6 @@

from PyPowerFlex import exceptions


def init_logger(log_level):
"""Initialize logger for PowerFlex client.
Expand Down Expand Up @@ -130,14 +129,20 @@ def prepare_params(params, dump=True):
return prepared


def check_version(version):
def is_version_3(version):
""" Check the API version.
:param version: Specifies the current API version
:return: True if API version is greater than or equal to 4.0
:return: True if API version is lesser than 4.0
:rtype: bool
"""
appliance_version = "4.0"
if version >= appliance_version:
if version < appliance_version:
return True
return False

def build_uri_with_params(uri, **url_params):
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]
if query_params:
uri += '?' + '&'.join(query_params)
return uri
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ python setup.py install
* Sdc
* FaultSet
* Volume
* ManagedDevice
* Deployment
* ServiceTemplate

#### Initialize PowerFlex client

Expand Down
Loading

0 comments on commit 3ffddc9

Please sign in to comment.