Skip to content

Commit

Permalink
Release version 240.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Oren Epshtain committed Feb 4, 2024
1 parent c45cd4a commit f58a9e7
Show file tree
Hide file tree
Showing 26 changed files with 923 additions and 45 deletions.
1 change: 1 addition & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Added isort and black and run on infinisdk folder
1aa81b3acec2145d69beb132bd5dab7ec79a1b3e
2b85e6e2a42fa1b0f3e7a4c6268fdf3428502a50
4 changes: 2 additions & 2 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ test-release:
- python -m pip install --upgrade pip
- pip install setuptools wheel twine
- python setup.py sdist bdist_wheel
- twine upload -u $TEST_PYPI_USER -p $TEST_PYPI_PASSWORD --verbose --non-interactive --repository-url https://test.pypi.org/legacy/ dist/*
- python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple infinisdk
- twine upload -u "__token__" -p $TEST_PYPI_API_TOKEN --verbose --non-interactive --repository-url https://test.pypi.org/legacy/ dist/*
- python -m pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple infinisdk
- python -c "import infinisdk"
only:
refs:
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
Version 240.1.1 (Released 2024-01-31)
-------------------------------------
* #16462: Add pagination for the new metadata of get all assigned entities
* #16368: Add RelatedSubObjectBinding
* #16348: Add created_by_schedule_name fields and default policy indicator
* #16335: Add created_by_schedule_name to dataset and cons_group in InfiniSDK
* #16318: Add support for snapshot policies enhancements, rename sso field
* #16306: Rename auth_link_url to sign_on_url in SSO in InfiniSDK
* #16287: Remove encryption_state field from LocalDrive in InfiniSDK [breaking change]
* #16284: Add missing ldap config fields to InfiniSDK
* #16280: Add tests and documentation for ldap config modify when passing an update dictionary
* #16231: Support snapshot policy enhancements
* #16223: Make snapshot policy suffix to contain a random part in its name
* #16198: Support config of SMB server capabilities
* #16155: Support SSA Express
* #16149: Support single sign on (SSO) identity providers management
* #16131: Support disable replication for a single snapshot
* #16044: Support block-ssh
* #15920: Support Snapshot Group (SG) replicate snapshots
* #15888: Add Snapshot Policies (Snap Scheduler)

Version 225.1.1 (Released 2023-05-07)
-------------------------------------
* #16212: Add infi.dtypes.nqn to requirements
Expand Down
14 changes: 14 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,20 @@ infinibox.smb_users
.. autoclass:: SMBUser
:members:

infinibox.snapshot_policies
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. automodule:: infinisdk.infinibox.snapshot_policy
.. autoclass:: SnapshotPolicy
:members:

infinibox.snapshot_policies.schedules
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. automodule:: infinisdk.infinibox.schedule
.. autoclass:: Schedule
:members:

infinibox.active_directory_domains
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
56 changes: 35 additions & 21 deletions doc/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ InfiniSDK represents system events through the *system.events* collection, which
>>> for event in system.events:
... print(event) # doctest: +ELLIPSIS
<...:Event id=1000, code=VOLUME_CREATED>
<...:Event id=1001, code=VOLUME_DELETED>
<...:Event id=1002, code=VOLUME_CREATED>
<...:Event id=1003, code=VOLUME_DELETED>
<...:Event id=1004, code=VOLUME_CREATED>
<...:Event id=1005, code=VOLUME_DELETED>
<...:Event id=1006, code=USER_LOGIN_SUCCESS>
<...:Event id=1000, code=SNAPSHOT_POLICY_CREATED>
<...:Event id=1001, code=SNAPSHOT_SCHEDULE_CREATED>
<...:Event id=1002, code=SNAPSHOT_SCHEDULE_ENABLED>
<...:Event id=1003, code=SNAPSHOT_SCHEDULE_CREATED>
<...:Event id=1004, code=SNAPSHOT_SCHEDULE_ENABLED>
<...:Event id=1005, code=VOLUME_CREATED>
<...:Event id=1006, code=VOLUME_DELETED>
<...:Event id=1007, code=VOLUME_CREATED>
<...:Event id=1008, code=VOLUME_DELETED>
<...:Event id=1009, code=VOLUME_CREATED>
<...:Event id=1010, code=VOLUME_DELETED>
<...:Event id=1011, code=USER_LOGIN_SUCCESS>
Sorting is determined by the system by default, but we can easily change that. For instance, we can order the events by descending id:
Expand All @@ -22,23 +27,28 @@ Sorting is determined by the system by default, but we can easily change that. F
>>> for event in system.events.find().sort(-system.events.fields.id):
... print(event) # doctest: +ELLIPSIS
<...:Event id=1006, code=USER_LOGIN_SUCCESS>
<...:Event id=1005, code=VOLUME_DELETED>
<...:Event id=1004, code=VOLUME_CREATED>
<...:Event id=1003, code=VOLUME_DELETED>
<...:Event id=1002, code=VOLUME_CREATED>
<...:Event id=1001, code=VOLUME_DELETED>
<...:Event id=1000, code=VOLUME_CREATED>
<...:Event id=1011, code=USER_LOGIN_SUCCESS>
<...:Event id=1010, code=VOLUME_DELETED>
<...:Event id=1009, code=VOLUME_CREATED>
<...:Event id=1008, code=VOLUME_DELETED>
<...:Event id=1007, code=VOLUME_CREATED>
<...:Event id=1006, code=VOLUME_DELETED>
<...:Event id=1005, code=VOLUME_CREATED>
<...:Event id=1004, code=SNAPSHOT_SCHEDULE_ENABLED>
<...:Event id=1003, code=SNAPSHOT_SCHEDULE_CREATED>
<...:Event id=1002, code=SNAPSHOT_SCHEDULE_ENABLED>
<...:Event id=1001, code=SNAPSHOT_SCHEDULE_CREATED>
<...:Event id=1000, code=SNAPSHOT_POLICY_CREATED>
We can also combine this with filtering. The following example filters by specific event code:

.. code-block:: python
>>> for event in system.events.find(code='VOLUME_CREATED').sort(-system.events.fields.id):
... print(event) # doctest: +ELLIPSIS
<...:Event id=1004, code=VOLUME_CREATED>
<...:Event id=1002, code=VOLUME_CREATED>
<...:Event id=1000, code=VOLUME_CREATED>
<...:Event id=1009, code=VOLUME_CREATED>
<...:Event id=1007, code=VOLUME_CREATED>
<...:Event id=1005, code=VOLUME_CREATED>
Example: Getting all Events Newer than a Specific Sequence Number
-----------------------------------------------------------------
Expand All @@ -48,7 +58,11 @@ Example: Getting all Events Newer than a Specific Sequence Number
>>> from infinisdk import Q
>>> for e in system.events.find(Q.seq_num>=1004):
... print(e) # doctest: +ELLIPSIS
<...:Event id=1004, code=VOLUME_CREATED>
<...:Event id=1005, code=VOLUME_DELETED>
<...:Event id=1006, code=USER_LOGIN_SUCCESS>
<...:Event id=1004, code=SNAPSHOT_SCHEDULE_ENABLED>
<...:Event id=1005, code=VOLUME_CREATED>
<...:Event id=1006, code=VOLUME_DELETED>
<...:Event id=1007, code=VOLUME_CREATED>
<...:Event id=1008, code=VOLUME_DELETED>
<...:Event id=1009, code=VOLUME_CREATED>
<...:Event id=1010, code=VOLUME_DELETED>
<...:Event id=1011, code=USER_LOGIN_SUCCESS>
2 changes: 2 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Contents:
filesystems
mappings
snapshots
snapshot_policies
cons_groups
users
system_configuration
Expand All @@ -48,6 +49,7 @@ Contents:
api
advanced_usage
hooks
smb_server_capabilities


Indices and tables
Expand Down
28 changes: 28 additions & 0 deletions doc/smb_server_capabilities.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
SMB Server Capabilities
=======================

These are SMB settings at the level of the tenant, so there is one configuration per tenant.

Getting Current Server Capabilities
-----------------------------------
You can get the current configuration by:

.. code-block:: python
>>> system.get_smb_server_capabilities() # doctest: +SKIP
The default tenant SMB server capabilities will be returned. You should expect the following fields to be returned:

* `min_smb_protocol`
* `max_smb_protocol`
* `smb_signing`
* `smb_encryption`

Updating Server Capabilities
----------------------------

To update a field, e.g. `encryption`:

.. code-block:: python
>>> system.update_smb_server_capabilities(smb_encryption="disabled") # doctest: +SKIP
69 changes: 69 additions & 0 deletions doc/snapshot_policies.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
Snapshot Policies
=================

This is a process that automates periodic creation of snapshots on any of the system's storage entities - filesystem, volume, and a consistency-group (CG). Snapshot policies define the rules for snapshot creation.

Creating a Policy
-----------------
A policy defines how and when to create snapshots. It contains a list of schedules which define when to create a snapshot.

To create a policy:

.. code-block:: python
>>> policy1 = system.snapshot_policies.create()
An optional parameters of `name` and `suffix` can be passed in creation. The suffix is a string which will be added to the snapshots' names.

Creating a Schedule
-------------------
After creating a policy you can create a schedule for creating the snapshot:

.. code-block:: python
>> from datetime import timedelta
>> schedule1 = policy1.schedules.create(name="every3hours",interval=timedelta(seconds=7200),retention=timedelta(seconds=3600))
In this example a snapshot will be taken every 7200 seconds and be retained for 3600 seconds (after which it will be deleted).

The default type of schedule is `periodic` but you can also specify `type=clock` to denote a specific day and time. To do that you need to specify 2 additional parameters:
1. `day_of_week` which can get string values of the days of the week ("sunday", "monday", etc.) or "all" for all the days in the week.
2. `time_of_day` which denotes the time in the day to perform the snapshot operation.

.. code-block:: python
>> from datetime import time
>> schedule2 = policy1.schedules.create(name="every3hours",type="clock",day_of_week="sunday",time_of_day=time(20, 30, 10),retention=timedelta(seconds=3600)) # doctest: +SKIP
In this example the snapshot will be taken every Sunday at 20:30:10 (8 PM, 30 minutes, 10 seconds).

A schedule cannot be updated. To make changes the schedule should be deleted and a new one with the change should be created.
Each policy can have up to 8 schedules.

Assigning Policies to Datasets
------------------------------
Snapshot policies can be assigned to datasets (of type master and snapshot): volumes, filesystems and CGs.
This dataset then will be snapshoted according to the policy schedule.

.. code-block:: python
>>> fs1 = system.filesystems.create(name="fs1", pool=pool)
>>> policy1.assign_entity(entity=fs1)
To unassign an entity from a policy you need to pass the entity instance that you want to unassign:

.. code-block:: python
>>> policy1.unassign_entity(entity=fs1)
To get all the assigned entities for a policy you can do:

.. code-block:: python
>>> policy1_entities = policy1.get_assigned_entities()
You can also pass either one or both of the desired page size and page number in case you have many entities:

.. code-block:: python
>>> policy1_entities = policy1.get_assigned_entities(page_size=1, page=1)
18 changes: 18 additions & 0 deletions doc/snapshot_policies.rst.doctest_context
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# -*- mode: python -*-
from contextlib import contextmanager
from infinisim.infinibox import Infinibox as Simulator
from infinisdk import InfiniBox

@contextmanager
def doctest_context():
simulator = Simulator()
simulator.activate()
system = InfiniBox(simulator, auth=('infinidat', '123456'))
system.login()
try:
yield {
"system": system,
"pool": system.pools.create(),
}
finally:
simulator.deactivate()
8 changes: 8 additions & 0 deletions doc/system_object.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,13 @@ The :meth:`infinisdk.infinibox.InfiniBox.get_model_name` method retrieves the mo
.. seealso:: :class:`infinisdk.infinibox.InfiniBox`

Opening and Closing SSH Ports
-----------------------------

You can open/close the SSH ports of the system as well as querying its status by:

.. code-block:: python
>>> system.open_ssh_ports() # doctest: +SKIP
>>> system.close_ssh_ports() # doctest: +SKIP
>>> system.get_ssh_ports_status() # doctest: +SKIP
74 changes: 74 additions & 0 deletions infinisdk/core/bindings.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from typing import Callable

from api_object_schema import ObjectAPIBinding
from munch import Munch
from sentinels import NOTHING

from .api.special_values import RawValue, SpecialValue
from .exceptions import InfiniSDKRuntimeException, InvalidUsageException
from .translators_and_types import address_type_factory, host_port_from_api

# pylint: disable=abstract-method
Expand Down Expand Up @@ -276,3 +279,74 @@ def get_value_from_api_value(self, system, objtype, obj, api_value):
if api_value == self._value_for_none or api_value is None:
return None
return getattr(system, self._collection_name).get_by_id_lazy(api_value["id"])


class RelatedSubObjectBinding(RelatedObjectBinding):
def __init__(
self, collection_name=None, value_for_none=0, child_collection_resolver=None
):
super().__init__(collection_name, value_for_none)
self.child_collection_resolver = child_collection_resolver

def get_value_from_api_value(self, system, objtype, obj, api_value):
if api_value == self._value_for_none or api_value is None:
return None
if self._collection_name is None:
raise InvalidUsageException("collection_name can't be None for this object")
assert (
"/" in self._collection_name
), "Subobjects need to be specified as 'parents/childs'"
parent_collection_name, child_collection_name = self._collection_name.split(
"/", maxsplit=1
)
assert (
"/" not in parent_collection_name
), f"Illegal name: {parent_collection_name}"
assert (
"/" not in child_collection_name
), f"Illegal name, {child_collection_name}"

def default_child_collection_resolver():
"""
The flow in this function is a default flow - try to get the child
collection and then the instance from the parent. However, in cases
where this will fail, particularly, when there is no parent nor child instances,
(e.g. can happen for the case of snapshot_policies/schedules.
A snapshot that is a member of an SG (snapshot group = snapshoted CG),
which in this case won't have the snapshot policy which created it because
snapshot policies cannot be assigned to SGs or to CG members)
a custom resolver with the specific colletion should be used.
For an example check the snapshot_policies/schedules case.
"""
parent_obj = getattr(system, parent_collection_name, None)
if parent_obj is None:
raise InfiniSDKRuntimeException(
f"No such collection ({parent_collection_name})"
)
parent_instance = getattr(
obj.get_parent(), f"get_{parent_obj.object_type.get_type_name()}"
)()
# The next line assumes that the collection name is a method on the parent class
child_collection = getattr(parent_instance, child_collection_name, None)
if child_collection is None and parent_instance is not None:
raise InfiniSDKRuntimeException(
f"{child_collection_name} is not a sub-collection of {parent_collection_name}"
)
elif child_collection is None and parent_instance is None:
raise RuntimeError(
"Both parent and child objects are None. You might want to use a different child_collection_resolver"
)
return child_collection

try:
resolved_child_collection = default_child_collection_resolver()
return resolved_child_collection.get_by_id(api_value)
except RuntimeError as e:
if not self.child_collection_resolver:
raise InvalidUsageException(
"default_child_collection_resolver have failed and no other child_collection_resolver was supplied"
) from e
assert isinstance(
self.child_collection_resolver, Callable
), f"child_collection_resolver should be a function, got {type(self.child_collection_resolver)} instead"
return self.child_collection_resolver(system, api_value)
Loading

0 comments on commit f58a9e7

Please sign in to comment.