diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..adf65615 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,96 @@ +name: CI + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + +jobs: + # ----------------- + # Linux job (pytest + flake8 + coverage + codecov) + # ----------------- + linux-tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [ "3.10", "3.11", "3.12" ] + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + pip install -r requirements.txt + pip install -r test-requirements.txt + pip install codecov + + - name: Run flake8 + if: matrix.python-version == '3.11' # run lint once + run: flake8 storops storops_test storops_comptest + + - name: Run pytest with coverage + run: | + pytest -n2 --cov=storops --cov-config coverage.ini \ + --cov-report=xml --cov-report=term \ + --junit-xml=junit-result.xml storops_test + + - name: Upload coverage to Codecov + if: matrix.python-version == '3.11' # upload once + uses: codecov/codecov-action@v5 + with: + files: ./coverage.xml + fail_ci_if_error: false + + # ----------------- + # Windows job (tox-based tests + pep8 once) + # ----------------- + windows-tests: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + python-version: [ "3.10", "3.11", "3.12" ] + toxenv: [ "py310", "py311", "py312", "pep8" ] + exclude: + # map toxenv to correct python versions only + - python-version: "3.10" + toxenv: py311 + - python-version: "3.10" + toxenv: py312 + - python-version: "3.11" + toxenv: py310 + - python-version: "3.11" + toxenv: py312 + - python-version: "3.12" + toxenv: py310 + - python-version: "3.12" + toxenv: py311 + # only run pep8 once (Python 3.11) + - python-version: "3.10" + toxenv: pep8 + - python-version: "3.12" + toxenv: pep8 + env: + TOXENV: ${{ matrix.toxenv }} + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools virtualenv wheel + python -m pip install tox + + - name: Run tox + run: tox diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f7faba84..00000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: python -python: - - "3.10" - - "3.11" -install: - - pip install codecov - - pip install -r requirements.txt - - pip install -r test-requirements.txt -script: - - flake8 storops storops_test storops_comptest - - "py.test -n2 --cov=storops --cov-config coverage.ini --cov-report=xml --cov-report term --junit-xml=junit-result.xml storops_test" -after_success: - - codecov -notifications: - email: - - yong.huang@dell.com diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index d3697959..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,49 +0,0 @@ -environment: - - matrix: - - # For Python versions available on Appveyor, see - # http://www.appveyor.com/docs/installed-software#python - # The list here is complete (excluding Python 2.6, which - # isn't covered by this document) at the time of writing. - - TOXENV: "pep8" - PYTHON: "C:\\Python37-x64" - DISTUTILS_USE_SDK: "1" - - TOXENV: "py36" - PYTHON: "C:\\Python36-x64" - DISTUTILS_USE_SDK: "1" - - TOXENV: "py37" - PYTHON: "C:\\Python37-x64" - DISTUTILS_USE_SDK: "1" - - TOXENV: "py38" - PYTHON: "C:\\Python38-x64" - DISTUTILS_USE_SDK: "1" - -install: - # We need wheel installed to build wheels - - echo Installed Pythons - - pip.exe list - - dir C:\Python* - - "%PYTHON%\\python.exe -m pip install tox" - # Try to workaround the issue: - # Expected version spec in enum34;python_version<"3.4" # BSD at ;python_version<"3.4" # BSD - # Upgrading virtualenv makes the pip inside tox being as latest as possible - - "%PYTHON%\\python.exe -m pip install --upgrade pip setuptools virtualenv" - -build: off - -test_script: - # Put your test command here. - # If you don't need to build C extensions on 64-bit Python 3.3 or 3.4, - # you can remove "build.cmd" from the front of the command, as it's - # only needed to support those cases. - # Note that you must use the environment variable %PYTHON% to refer to - # the interpreter you're using - Appveyor does not do anything special - # to put the Python evrsion you want to use on PATH. - - "%PYTHON%\\Scripts\\tox.exe" - -#on_success: -# You can use this step to upload your artifacts to a public website. -# See Appveyor's documentation for more details. Or you can simply -# access your wheels from the Appveyor "artifacts" tab for your build. - diff --git a/requirements.txt b/requirements.txt index cb82b482..c4c33be1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,11 @@ -requests>=2.32.4 # Apache-2.0, cinder, manila -PyYAML>=6.0.2 # MIT, cinder, manila -six>=1.9.0 # MIT, cinder, manila +requests # Apache-2.0, cinder, manila +PyYAML # MIT, cinder, manila +six # MIT, cinder, manila enum34 # BSD -python-dateutil>=2.4.2 # BSD -retryz>=0.1.8 # Apache-2.0 -cachez>=0.1.0 # Apache-2.0 -bitmath>=1.3.0 # MIT -urllib3>=2.5.0 # MIT +python-dateutil # BSD +retryz # Apache-2.0 +cachez # Apache-2.0 +bitmath # MIT +urllib3 # MIT +setuptools \ No newline at end of file diff --git a/storops/unity/parser_configs.yaml b/storops/unity/parser_configs.yaml index 9be92a17..dbbaff21 100644 --- a/storops/unity/parser_configs.yaml +++ b/storops/unity/parser_configs.yaml @@ -2060,6 +2060,7 @@ UnityReplicationSession: - label: name - label: networkStatus converter: ReplicationSessionNetworkStatusEnum + - label: noAsyncSnapReplication - label: remoteSystem converter: UnityRemoteSystem - label: replicationResourceType diff --git a/storops/unity/resource/filesystem.py b/storops/unity/resource/filesystem.py index abc31923..68b731ee 100644 --- a/storops/unity/resource/filesystem.py +++ b/storops/unity/resource/filesystem.py @@ -46,7 +46,8 @@ class UnityFileSystem(UnityResource): def create(cls, cli, pool, nas_server, name, size, proto=None, is_thin=None, tiering_policy=None, user_cap=False, is_compression=None, access_policy=None, - locking_policy=None, description=None): + locking_policy=None, description=None, + is_advanced_dedup_enabled=None): pool_clz = storops.unity.resource.pool.UnityPool nas_server_clz = storops.unity.resource.nas_server.UnityNasServer @@ -65,7 +66,8 @@ def create(cls, cli, pool, nas_server, name, size, proto=None, tiering_policy=tiering_policy, is_compression=is_compression, access_policy=access_policy, - locking_policy=locking_policy) + locking_policy=locking_policy, + is_advanced_dedup_enabled=is_advanced_dedup_enabled) req_body = cli.make_body(allow_empty=True, name=name, description=description, @@ -80,7 +82,8 @@ def create(cls, cli, pool, nas_server, name, size, proto=None, def modify(self, size=None, is_thin=None, tiering_policy=None, user_cap=False, is_compression=None, access_policy=None, locking_policy=None, description=None, - cifs_fs_parameters=None, snap_schedule_parameters=None): + cifs_fs_parameters=None, snap_schedule_parameters=None, + is_advanced_dedup_enabled=None): sr = self.storage_resource if sr is None: raise ValueError('storage resource for filesystem {} not found.' @@ -94,7 +97,8 @@ def modify(self, size=None, is_thin=None, tiering_policy=None, tiering_policy=tiering_policy, is_compression=is_compression, access_policy=access_policy, - locking_policy=locking_policy) + locking_policy=locking_policy, + is_advanced_dedup_enabled=is_advanced_dedup_enabled) params = {} if fs_param: @@ -244,15 +248,19 @@ def has_snap(self, ignore_system_snap=False): snaps = filter(lambda s: not s.is_system_snap, snaps) return len(list(snaps)) > 0 - def replicate_with_dst_resource_provisioning(self, max_time_out_of_sync, - dst_pool_id, - dst_fs_name=None, - remote_system=None, - replication_name=None, - dst_size=None, - is_dst_thin=None, - dst_tiering_policy=None, - is_dst_compression=None): + def replicate_with_dst_resource_provisioning( + self, max_time_out_of_sync, + dst_pool_id, + dst_fs_name=None, + remote_system=None, + replication_name=None, + dst_size=None, + is_dst_thin=None, + dst_tiering_policy=None, + is_dst_compression=None, + no_async_snap_replication=None, + hourly_snap_replication_policy=None, + daily_snap_replication_policy=None): """ Creates a replication session with destination filesystem provisioning. @@ -274,6 +282,16 @@ def replicate_with_dst_resource_provisioning(self, max_time_out_of_sync, destination filesystem. :param is_dst_compression: indicates whether destination filesystem is compression enabled or not. + :param no_async_snap_replication: whether or not snap replication is + enabled in asynchronous replication session. When enabled, snap + replication is controlled by snap replication policy setting or + user action. + :param hourly_snap_replication_policy: `UnitySnapReplicationPolicy` + object. The policy for replicating hourly scheduled snaps of the + source resource. + :param daily_snap_replication_policy: `UnitySnapReplicationPolicy` + object. The policy for replicating daily scheduled snaps of the + source resource. :return: created replication session. """ @@ -294,20 +312,33 @@ def replicate_with_dst_resource_provisioning(self, max_time_out_of_sync, return UnityReplicationSession.create_with_dst_resource_provisioning( self._cli, self.storage_resource.get_id(), dst_resource, max_time_out_of_sync, - remote_system=remote_system, name=replication_name) + remote_system=remote_system, name=replication_name, + no_async_snap_replication=no_async_snap_replication, + hourly_snap_replication_policy=hourly_snap_replication_policy, + daily_snap_replication_policy=daily_snap_replication_policy) @staticmethod def prepare_fs_parameters(**kwargs): @version('<4.3') - def make_compression_body(is_compression=None): + def make_compression_body(is_compression=None, + is_advanced_dedup_enabled=None): return UnityClient.make_body( allow_empty=True, isCompressionEnabled=is_compression) - @version('>=4.3') # noqa - def make_compression_body(is_compression=None): + @version('>=4.3') + def make_compression_body(is_compression=None, # noqa: F811 + is_advanced_dedup_enabled=None): return UnityClient.make_body( allow_empty=True, isDataReductionEnabled=is_compression) + @version('>=4.5') + def make_compression_body(is_compression=None, # noqa: F811 + is_advanced_dedup_enabled=None): + return UnityClient.make_body( + allow_empty=True, + isDataReductionEnabled=is_compression, + isAdvancedDedupEnabled=is_advanced_dedup_enabled) + access_policy = kwargs.get('access_policy') locking_policy = kwargs.get('locking_policy') supported_protocols = kwargs.get('supported_protocols') @@ -332,7 +363,9 @@ def make_compression_body(is_compression=None): fs_param['fastVPParameters'] = UnityClient.make_body( allow_empty=True, tieringPolicy=tiering_policy) - compression_body = make_compression_body(kwargs.get('is_compression')) + compression_body = make_compression_body( + kwargs.get('is_compression'), + kwargs.get('is_advanced_dedup_enabled')) fs_param.update(compression_body) return fs_param diff --git a/storops/unity/resource/replication_session.py b/storops/unity/resource/replication_session.py index e79e313d..d5def1eb 100755 --- a/storops/unity/resource/replication_session.py +++ b/storops/unity/resource/replication_session.py @@ -267,7 +267,8 @@ def modify(self, max_time_out_of_sync=None, name=None, hourly_snap_replication_policy=None, daily_snap_replication_policy=None, src_spa_interface=None, src_spb_interface=None, - dst_spa_interface=None, dst_spb_interface=None): + dst_spa_interface=None, dst_spb_interface=None, + no_async_snap_replication=None): """ Modifies properties of a replication session. @@ -281,6 +282,10 @@ def modify(self, max_time_out_of_sync=None, name=None, :param src_spb_interface: same as the one in `create` method. :param dst_spa_interface: same as the one in `create` method. :param dst_spb_interface: same as the one in `create` method. + :param no_async_snap_replication: whether or not snap replication is + enabled in asynchronous replication session. When enabled, snap + replication is controlled by snap replication policy setting or + user action. """ req_body = self._cli.make_body( maxTimeOutOfSync=max_time_out_of_sync, name=name, @@ -289,7 +294,8 @@ def modify(self, max_time_out_of_sync=None, name=None, srcSPAInterface=src_spa_interface, srcSPBInterface=src_spb_interface, dstSPAInterface=dst_spa_interface, - dstSPBInterface=dst_spb_interface) + dstSPBInterface=dst_spb_interface, + noAsyncSnapReplication=no_async_snap_replication) resp = self.action('modify', **req_body) resp.raise_if_err() diff --git a/test-requirements.txt b/test-requirements.txt index 2544403a..a2405291 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,13 +1,10 @@ -coverage>=4.0 -flake8>=2.2.0 -mock>=1.0.1 -PyHamcrest>=1.8.5 -pytest>=4.4.0,<6.0.0 -pytest>=6.0.0 -pytest-cov>=2.1.0,<2.10.1 -pytest-cov>=2.10.1 -pytest-xdist>=1.13.1,<2.0.0 -pytest-xdist>=2.0.0 -xmltodict>=0.9.2 -fasteners>=0.12.0 -ddt>=1.0.1 # MIT +coverage +flake8 +mock +PyHamcrest +pytest +pytest-cov +pytest-xdist +xmltodict +fasteners +ddt # MIT diff --git a/tox.ini b/tox.ini index b8ed1700..246ce4df 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,10 @@ [tox] -envlist = py27,py3{6,7,8,9,10},pep8 +envlist = py3{9,10,11,12},pep8 [testenv] deps = - pip!=19.0.0,!=19.0.1,>=18.1 + pip -rrequirements.txt -rtest-requirements.txt @@ -22,7 +22,7 @@ norecursedirs = .tox .git [testenv:pep8] deps = - pip!=19.0.0,!=19.0.1,>=18.1 + pip flake8 commands = flake8 storops storops_test storops_comptest