Skip to content

Commit

Permalink
Merge pull request #9 from dell/RTSv1.7.0
Browse files Browse the repository at this point in the history
Release version 1.7.0 for PyPowerFlex
  • Loading branch information
Jennifer-John authored Mar 28, 2023
2 parents 64b2517 + 70d7f6b commit 031389a
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 4 deletions.
5 changes: 4 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# PyPowerFlex Change Log

## Version 1.6.0 - released on 20/12/22
## Version 1.7.0 - released on 31/03/23
- Added block provisioning operations includes getting details, adding, pause, resume and removing a replication pair.

## Version 1.6.0 - released on 28/12/22
- Added block provisioning operations includes getting details, creating, modifying, creating snapshots, pause, resume, freeze, unfreeze,
activate, inactivate and deleting a replication consistency group.

Expand Down
4 changes: 3 additions & 1 deletion PyPowerFlex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class PowerFlexClient:
'system',
'volume',
'utility',
'replication_consistency_group'
'replication_consistency_group',
'replication_pair'
)

def __init__(self,
Expand Down Expand Up @@ -90,6 +91,7 @@ def initialize(self):
self.__add_storage_entity('volume', objects.Volume)
self.__add_storage_entity('utility', objects.PowerFlexUtility)
self.__add_storage_entity('replication_consistency_group', objects.ReplicationConsistencyGroup)
self.__add_storage_entity('replication_pair', objects.ReplicationPair)
utils.init_logger(self.configuration.log_level)
if version.parse(self.system.api_version()) < version.Version('3.0'):
raise exceptions.PowerFlexClientException(
Expand Down
4 changes: 3 additions & 1 deletion PyPowerFlex/objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from PyPowerFlex.objects.volume import Volume
from PyPowerFlex.objects.utility import PowerFlexUtility
from PyPowerFlex.objects.replication_consistency_group import ReplicationConsistencyGroup
from PyPowerFlex.objects.replication_pair import ReplicationPair


__all__ = [
Expand All @@ -39,5 +40,6 @@
'System',
'Volume',
'PowerFlexUtility',
'ReplicationConsistencyGroup'
'ReplicationConsistencyGroup',
'ReplicationPair'
]
10 changes: 10 additions & 0 deletions PyPowerFlex/objects/replication_consistency_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ def rename_rcg(self, rcg_id, new_name):
return self._perform_entity_operation_based_on_action\
(rcg_id, "rename", params=params)

def get_replication_pairs(self, rcg_id):
"""Get replication pairs of PowerFlex RCG.
:param rcg_id: str
:return: dict
"""

return self.get_related(rcg_id,
'ReplicationPair')

def get_all_statistics(self, api_version_less_than_3_6):
"""list statistics of all replication consistency groups for PowerFlex.
:param api_version_less_than_3_6: bool
Expand Down
103 changes: 103 additions & 0 deletions PyPowerFlex/objects/replication_pair.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Copyright (c) 2023 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


LOG = logging.getLogger(__name__)


class ReplicationPair(base_client.EntityRequest):
def get_statistics(self, id):
"""Retrieve statistics for the specified ReplicationPair object.
:type id: str
:rtype: dict
"""

return self.get_related(id,
'Statistics')

def add(self,
source_vol_id,
dest_vol_id,
rcg_id,
copy_type,
name=None):
"""Add replication pair to PowerFlex RCG.
:param source_vol_id: str
:param dest_vol_id: str
:param rcg_id: str
:param copy_type: str
:type name: str
:return: dict
"""

params = dict(
sourceVolumeId=source_vol_id,
destinationVolumeId=dest_vol_id,
replicationConsistencyGroupId=rcg_id,
copyType=copy_type,
name=name
)

return self._create_entity(params)

def remove(self, id):
"""Remove replication pair of PowerFlex RCG.
:param id: str
:return: None
"""
return self._delete_entity(id)

def pause(self, id):
"""Pause the progress of the specified ReplicationPair's initial copy.
:param id: str
:return: dict
"""
return self._perform_entity_operation_based_on_action\
(id, "pausePairInitialCopy", add_entity=False)

def resume(self, id):
"""Resume initial copy of the ReplicationPair.
:param id: str
:return: dict
"""
return self._perform_entity_operation_based_on_action\
(id, "resumePairInitialCopy", add_entity=False)

def get_all_statistics(self):
"""Retrieve statistics for all ReplicationPair objects.
:return: dict
"""
r, response = self.send_post_request(self.list_statistics_url,
entity=self.entity,
action="querySelectedStatistics")
if r.status_code != requests.codes.ok:
msg = ('Failed to list statistics for all ReplicationPair objects. '
'Error: {response}'.format(response=response))
LOG.error(msg)
raise exceptions.PowerFlexClientException(msg)

return response
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ python setup.py install
* Sds
* SnapshotPolicy
* ReplicationConsistencyGroup
* ReplicationPair
* System
* StoragePool
* AccelerationPool
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

setup(
name='PyPowerFlex',
version='1.6.0',
version='1.7.0',
description='Python library for Dell PowerFlex',
author='Ansible Team at Dell',
author_email='ansible.team@dell.com',
Expand Down
6 changes: 6 additions & 0 deletions tests/test_replication_consistency_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ def setUp(self):
{'id': self.fake_rcg_id},
'/types/ReplicationConsistencyGroup/instances/action/querySelectedStatistics':
{},
'/instances/ReplicationConsistencyGroup::{}'
'/relationships/ReplicationPair'.format(self.fake_rcg_id):
{'id': self.fake_rcg_id},
},
self.RESPONSE_MODE.Invalid: {
'/types/ReplicationConsistencyGroup/instances':
Expand Down Expand Up @@ -132,6 +135,9 @@ def test_modify_target_volume_access_mode(self):
def test_rename_rcg(self):
self.client.replication_consistency_group.rename_rcg(self.fake_rcg_id, new_name="rename")

def test_get_replication_pairs(self):
self.client.replication_consistency_group.get_replication_pairs(self.fake_rcg_id)

def test_get_all_statistics(self):
self.client.replication_consistency_group.get_all_statistics(True)

Expand Down
84 changes: 84 additions & 0 deletions tests/test_replication_pair.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright (c) 2023 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.

from PyPowerFlex import exceptions
from PyPowerFlex.objects import replication_pair
import tests


class TestReplicationPairClient(tests.PyPowerFlexTestCase):
def setUp(self):
super(TestReplicationPairClient, self).setUp()
self.client.initialize()
self.fake_replication_pair_id = '1'

self.MOCK_RESPONSES = {
self.RESPONSE_MODE.Valid: {
'/types/ReplicationPair/instances':
{'id': self.fake_replication_pair_id},
'/instances/ReplicationPair::{}'.format(self.fake_replication_pair_id):
{'id': self.fake_replication_pair_id},
'/instances/ReplicationPair::{}'
'/action/removeReplicationPair'.format(self.fake_replication_pair_id):
{},
'/instances/ReplicationPair::{}'
'/action/pausePairInitialCopy'.format(self.fake_replication_pair_id):
{'id': self.fake_replication_pair_id},
'/instances/ReplicationPair::{}'
'/action/resumePairInitialCopy'.format(self.fake_replication_pair_id):
{'id': self.fake_replication_pair_id},
'/types/ReplicationPair/instances/action/querySelectedStatistics':
{},
},
self.RESPONSE_MODE.Invalid: {
'/types/ReplicationPair/instances':
{},
}
}

def test_add_replication_pair(self):
self.client.replication_pair.add\
(source_vol_id='1', dest_vol_id='1',
rcg_id='1', copy_type='OnlineCopy', name='test')

def test_remove_replication_pair(self):
self.client.replication_pair.remove(self.fake_replication_pair_id)

def test_pause_online_copy(self):
self.client.replication_pair.pause(self.fake_replication_pair_id)

def test_resume_online_copy(self):
self.client.replication_pair.resume(self.fake_replication_pair_id)

def test_get_all_statistics(self):
self.client.replication_pair.get_all_statistics()

def test_add_replication_pair_bad_status(self):
with self.http_response_mode(self.RESPONSE_MODE.BadStatus):
self.assertRaises(exceptions.PowerFlexFailCreating,
self.client.replication_pair.add,
source_vol_id='1', dest_vol_id='1',
rcg_id='1', copy_type='OnlineCopy', name='test')

def test_remove_replication_pair_bad_status(self):
with self.http_response_mode(self.RESPONSE_MODE.BadStatus):
self.assertRaises(exceptions.PowerFlexFailDeleting,
self.client.replication_pair.remove,
self.fake_replication_pair_id)

def test_get_all_statistics_bad_status(self):
with self.http_response_mode(self.RESPONSE_MODE.BadStatus):
self.assertRaises(exceptions.PowerFlexClientException,
self.client.replication_pair.get_all_statistics)

0 comments on commit 031389a

Please sign in to comment.