* Removed these methods:
access_group_add_initiator() # replaced by access_group_edit()
access_group_del_initiator() # replaced by access_group_edit()
access_group_grant() # replaced by volume_mask()
access_group_revoke() # replaced by volume_unmask()
access_groups_granted_to_volume # replaced by access_groups()
initiators_granted_to_volume() # No need.
* Renamed these methods:
access_group_del() -> access_group_delete()
access_group_list() -> access_groups()
* Added these methods:
volume_mask()
volume_unmask()
mask_infos()
* Removed this class:
lsm.Initiator
* Added this class:
lsm.MaskInfo
* Updated this class:
lsm.AccessGroup
* Add property 'optional_data', 'init_type'.
* Renam property 'initiators' to 'init_ids'.
* No doxygen document included as we have better one.
* Method document copied from API document.
* Purged private constants and methods of lsm.AccessGroup about value
converting. They have been moved to lsmcli_data_display.py.
Signed-off-by: Gris Ge <***@redhat.com>
---
lsm/lsm/__init__.py | 6 +-
lsm/lsm/_client.py | 596 +++++++++++++++++++++++++++++++++++++---------------
lsm/lsm/_common.py | 45 ++++
lsm/lsm/_data.py | 168 ++++++++-------
4 files changed, 563 insertions(+), 252 deletions(-)
diff --git a/lsm/lsm/__init__.py b/lsm/lsm/__init__.py
index 0635b7e..836ab75 100644
--- a/lsm/lsm/__init__.py
+++ b/lsm/lsm/__init__.py
@@ -4,9 +4,9 @@ from version import VERSION
from _common import Error, Info, LsmError, ErrorLevel, ErrorNumber, \
JobStatus, uri_parse, md5, Proxy, size_bytes_2_size_human
-from _data import Initiator, Disk, \
- Volume, Pool, System, FileSystem, FsSnapshot, NfsExport, BlockRange, \
- AccessGroup, OptionalData, Capabilities, txt_a
+from _data import Disk, Volume, Pool, System, FileSystem, FsSnapshot, \
+ NfsExport, BlockRange, AccessGroup, OptionalData, Capabilities, MaskInfo, \
+ txt_a
from _iplugin import IPlugin, IStorageAreaNetwork, INetworkAttachedStorage, \
INfs
diff --git a/lsm/lsm/_client.py b/lsm/lsm/_client.py
index a8a30c2..fdfd542 100644
--- a/lsm/lsm/_client.py
+++ b/lsm/lsm/_client.py
@@ -19,9 +19,9 @@ import time
import os
import unittest
from lsm import (Volume, NfsExport, Capabilities, Pool, System,
- Initiator, Disk, AccessGroup, FileSystem, FsSnapshot,
+ Disk, AccessGroup, FileSystem, FsSnapshot,
uri_parse, LsmError, JobStatus, ErrorNumber,
- INetworkAttachedStorage)
+ INetworkAttachedStorage, MaskInfo)
from _common import return_requires as _return_requires
from _common import UDS_PATH as _UDS_PATH
@@ -427,17 +427,6 @@ class Client(INetworkAttachedStorage):
"""
return self._tp.rpc('systems', _del_self(locals()))
- ## Returns an array of initiator objects
- # @param self The this pointer
- # @param flags Reserved for future use, must be zero.
- # @returns An array of initiator objects.
- @_return_requires([Initiator])
- def initiators(self, flags=0):
- """
- Return an array of initiator objects
- """
- return self._tp.rpc('initiators', _del_self(locals()))
-
## Register a user/password for the specified initiator for CHAP
# authentication.
# Note: If you pass an empty user and password the expected behavior is to
@@ -459,57 +448,6 @@ class Client(INetworkAttachedStorage):
"""
return self._tp.rpc('iscsi_chap_auth', _del_self(locals()))
- ## Grants access so an initiator can read/write the specified volume.
- # @param self The this pointer
- # @param initiator_id The iqn, WWID etc.
- # @param initiator_type Enumerated initiator type
- # @param volume Volume to grant access to
- # @param access Enumerated access type
- # @param flags Reserved for future use, must be zero
- # @returns None on success, throws LsmError on errors.
- @_return_requires(None)
- def initiator_grant(self, initiator_id, initiator_type, volume, access,
- flags=0):
- """
- Allows an initiator to access a volume.
- """
- return self._tp.rpc('initiator_grant', _del_self(locals()))
-
- ## Revokes access for a volume for the specified initiator.
- # @param self The this pointer
- # @param initiator The iqn, WWID etc.
- # @param volume The volume to revoke access for
- # @param flags Reserved for future use, must be zero
- # @returns None on success, throws LsmError on errors.
- @_return_requires(None)
- def initiator_revoke(self, initiator, volume, flags=0):
- """
- Revokes access to a volume for the specified initiator
- """
- return self._tp.rpc('initiator_revoke', _del_self(locals()))
-
- ## Returns a list of volumes that are accessible from the specified
- # initiator.
- # @param self The this pointer
- # @param initiator The initiator object
- # @param flags Reserved for future use, must be zero
- @_return_requires([Volume])
- def volumes_accessible_by_initiator(self, initiator, flags=0):
- """
- Returns a list of volumes that the initiator has access to.
- """
- return self._tp.rpc('volumes_accessible_by_initiator',
- _del_self(locals()))
-
- ## Returns a list of initiators that have access to the specified volume.
- # @param self The this pointer
- # @param volume The volume in question
- # @param flags Reserved for future use, must be zero
- # @returns List of initiators
- @_return_requires([Initiator])
- def initiators_granted_to_volume(self, volume, flags=0):
- return self._tp.rpc('initiators_granted_to_volume', _del_self(locals()))
-
## Returns an array of volume objects
# @param self The this pointer
# @param flags Reserved for future use, must be zero.
@@ -658,6 +596,197 @@ class Client(INetworkAttachedStorage):
"""
return self._tp.rpc('volume_offline', _del_self(locals()))
+ @_return_requires([MaskInfo])
+ def mask_infos(self, search_key=None, search_value=None, flags=0):
+ """
+ lsm.Client.mask_infos(self, search_key=None, search_value=None,
+ flags=0)
+
+ Usage:
+ Change the initiators of certain access group.
+ If 'search_key' and 'search_value' is defined, only system matches
+ will be contained in return list.
+ Valid search keys are stored in lsm.MaskInfo.SUPPORTED_SEARCH_KEYS:
+ id # Search lsm.AccessGroup.id property
+ volume_id # Search lsm.AccessGroup.volume_id property
+ access_group_id # Search lsm.AccessGroup.access_group_id
+ # property
+ system_id # Search mask info on given system.
+ When no matches found, return a empty list
+ Parameters:
+ search_key # String. The key name for the search
+ search_value # The value of search_key to match
+ flags # Integer, Bit Map(Check Appendix.D for detail).
+ # Valid value:
+ # lsm.MaskInfo.FLAG_RETRIEVE_OPTIONAL_DATA
+ # # Return lsm.MaskInfo object will contain
+ # # optional properties. But currently,
+ # # there is no optional property for
+ # # lsm.MaskInfo yet, this will make no
+ # # difference.
+ Returns:
+ [lsm.MaskInfo,] # A list of lsm.MaskInfo objects.
+ or
+ [] # Nothing found.
+ Exception:
+ LsmError
+ lsm.ErrorNumber.PLUGIN_ERROR
+ # Plugin error when query. Please report a bug.
+ lsm.ErrorNumber.MASK_QUERY_INVALID_SEARCH_KEY
+ # Got invalid search key.
+ Sample:
+ lsm_cli_obj = lsm.Client('sim://')
+ lsm_mask_infos = lsm.Client.mask_infos()
+ Capability:
+ lsm.Capabilities.MASK_INFOS
+ # Indicate current systems support query mask info via
+ # lsm.Client.mask_infos() method.
+ lsm.Capabilities.MASK_INFOS_QUICK_SEARCH
+ # This capability indicate plugin can perform a quick
+ # search without pull all data from storage system.
+ # Without this capability, plugin will pull all data from
+ # storage system to search given key. It only save socket data
+ # transfer between plugin and user API. If user want to
+ # do multiple search, it is suggested to query all mask info
+ # without 'search_key' parameter, then filter in user's code
+ # space.
+ """
+ if search_key and search_key not in MaskInfo.SUPPORTED_SEARCH_KEYS:
+ raise LsmError(ErrorNumber.MASK_QUERY_INVALID_SEARCH_KEY,
+ "Invalid search key %s" % search_key)
+
+ return self._tp.rpc('mask_infos',
+ {"search_key":search_key,
+ "search_value":search_value,
+ "flags":flags})
+
+ @_return_requires(MaskInfo)
+ def volume_mask(self, volume, access_group, access_type, flags=0):
+ """
+ lsm.Client.volume_mask(self, volume, access_group, access_type,
+ flags=0)
+
+ Usage:
+ Mask certain volume to defined access group.
+ To change access_type of existing mask info, just invoke this
+ method with new access_type.
+ Storage will never mask single volume to single access group with
+ two or more LUN IDs, duplicated call of this method will not
+ resulting mulitpal masking for single voluem to single access
+ group.
+ For those storage array which does not support changing existing
+ masking relationship, they will deleting existing and creating new
+ one.
+ Parameters:
+ volume # Object of lsm.Volume
+ access_group # Object of lsm.AccessGroup
+ access_type # Integer. Check lsm.MaskInfo.access_type property.
+ flags # Reserved for future use, should be 0.
+ Returns:
+ lsm.MaskInfo # Object of lsm.MaskInfo.
+ Exception:
+ LsmError
+ lsm.ErrorNumber.PLUGIN_ERROR
+ # Plugin error when query. Please report a bug.
+ lsm.ErrorNumber.VOL_MASK_NO_SUPPORT
+ # Does not support
+ lsm.ErrorNumber.VOL_MASK_INVALID_VOLUME
+ # Invalid lsm.Volume
+ lsm.ErrorNumber.VOL_MASK_INVALID_ACCESS_GROUP
+ # Invalid lsm.AccessGroup
+ lsm.ErrorNumber.VOL_MASK_NO_SUPPORT_ACCESS_TYPE_RW
+ # RW access type is not support by storage system
+ lsm.ErrorNumber.VOL_MASK_NO_SUPPORT_ACCESS_TYPE_RO
+ # RO access type is not support by storage system
+ lsm.ErrorNumber.VOL_MASK_NOT_ALLOW_ACCESS_TYPE_RW
+ # Defined volume does not allow RW access
+ # Example: DR(Disaster Recover) site volumes does not
+ # allow RW access.
+ lsm.ErrorNumber.VOL_MASK_NOT_ALLOW_ACCESS_TYPE_RO
+ # Defined volume does not allow RO access.
+ Sample:
+ lsm_cli_obj = lsm.Client('sim://')
+ lsm_mask_infos = lsm.Client.mask_infos()
+ Capability:
+ lsm.Capabilities.VOLUME_MASK
+ # Indicate current systems support volume masking via
+ # lsm.Client.volume_mask() method.
+ """
+ return self._tp.rpc('volume_mask', _del_self(locals()))
+
+ @_return_requires(None)
+ def volume_unmask(self, mask_info=None, volume=None, access_group=None,
+ flags=0):
+ """
+ lsm.Client.volume_unmask(self, mask_info=None, volume=None,
+ access_group=None, flags=0)
+
+ Usage:
+ Unmask the volume.
+ This method support two way of unmask:
+ * lsm.Client.volume_unmask(self, mask_info=lsm.MaskInfo, flags=0)
+ * lsm.Client.volume_unmask(self, volume=lsm.Volume,
+ access_group=lsm.AccessGroup, flags=0)
+ The 'mask_info' parameter should not be used with 'volume' or
+ access_group parameters, a LsmError will be raise if so.
+ If define volume is not masked to access group, this method will
+ do nothing but return.
+ Parameters:
+ mask_info # Object of lsm.Maskino
+ volume # Object of lsm.Volume
+ access_group # Object of lsm.AccessGroup
+ flags # Reserved for future use, should be 0.
+ Returns:
+ None # Unmask done without error.
+ Exception:
+ LsmError
+ lsm.ErrorNumber.PLUGIN_ERROR
+ # Plugin error when query. Please report a bug.
+ lsm.ErrorNumber.VOL_UNMASK_INVALID_PARAMETER
+ # Neither 'mask_info' nor ('volume' and 'access_group')
+ # is define.
+ lsm.ErrorNumber.VOL_UNMASK_INVALID_MASK_INFO
+ # Invalid lsm.MaskInfo
+ lsm.ErrorNumber.VOL_UNMASK_INVALID_VOLUME
+ # Invalid lsm.Volume
+ lsm.ErrorNumber.VOL_UNMASK_INVALID_ACCESS_GROUP
+ # Invalid lsm.AccessGroup
+ lsm.ErrorNumber.VOL_UNMASK_NO_SUPPORT
+ # Does not support volume unmask.
+ lsm.ErrorNumber.VOL_UNMASK_CONFLICT_PARAMETERS
+ # Got 'mask_info' parameters with 'volume' or
+ # 'access_group' parameters
+ lsm.ErrorNumber.VOL_UNMASK_MISSING_VOLUME
+ # Only got 'access_group' parameter defined.
+ lsm.ErrorNumber.VOL_UNMASK_MISSING_ACCESS_GROUP
+ # Only got 'volume' parameter defined.
+ Sample:
+ lsm_cli_obj = lsm.Client('sim://')
+ lsm_mask_infos = lsm.Client.mask_infos()
+ Capability:
+ lsm.Capabilities.VOLUME_UNMASK
+ # Indicate current systems support volume unmasking via
+ # lsm.Client.volume_unmask() method.
+ """
+ if (mask_info is not None and volume is not None) or \
+ (mask_info is not None and access_group is not None):
+ raise LsmError(ErrorNumber.VOL_UNMASK_CONFLICT_PARAMETERS,
+ "Argument conflicting: both 'mask_info' and "
+ "('volume' or/and 'access_group') are defined.")
+ elif volume is not None and access_group is None:
+ raise LsmError(ErrorNumber.VOL_UNMASK_MISSING_ACCESS_GROUP,
+ "Only 'volume' is defined in volume_unmask()"
+ "parameter, missing 'access_group'.")
+ elif access_group is not None and volume is None:
+ raise LsmError(ErrorNumber.VOL_UNMASK_MISSING_VOLUME,
+ "Only 'access_group' is defined in volume_unmask()"
+ "parameter, missing 'volume'.")
+ elif mask_info is None and volume is None:
+ raise LsmError(ErrorNumber.VOL_UNMASK_INVALID_PRAMETER,
+ "Neither 'mask_info' nor ('volume' and "
+ "'access_group') is defined")
+ return self._tp.rpc('volume_unmask', _del_self(locals()))
+
## Returns an array of disk objects
# @param self The this pointer
# @param flags When equal to DISK.RETRIEVE_FULL_INFO
@@ -672,128 +801,251 @@ class Client(INetworkAttachedStorage):
"""
return self._tp.rpc('disks', _del_self(locals()))
- ## Access control for allowing an access group to access a volume
- # @param self The this pointer
- # @param group The access group
- # @param volume The volume to grant access to
- # @param access The desired access
- # @param flags Reserved for future use, must be zero.
- # @returns None on success, throws LsmError on errors.
- @_return_requires(None)
- def access_group_grant(self, group, volume, access, flags=0):
- """
- Allows an access group to access a volume.
- """
- return self._tp.rpc('access_group_grant', _del_self(locals()))
-
- ## Revokes access to a volume to initiators in an access group
- # @param self The this pointer
- # @param group The access group
- # @param volume The volume to grant access to
- # @param flags Reserved for future use, must be zero.
- # @returns None on success, throws LsmError on errors.
- @_return_requires(None)
- def access_group_revoke(self, group, volume, flags=0):
- """
- Revokes access for an access group for a volume
- """
- return self._tp.rpc('access_group_revoke', _del_self(locals()))
-
- ## Returns a list of access group objects
- # @param self The this pointer
- # @param flags Reserved for future use, must be zero.
- # @returns List of access groups
@_return_requires([AccessGroup])
- def access_groups(self, flags=0):
- """
- Returns a list of access groups
- """
- return self._tp.rpc('access_group_list', _del_self(locals()))
-
- ## Creates an access a group with the specified initiator in it.
+ def access_groups(self, search_key=None, search_value=None, flags=0):
+ """
+ lsm.Client.access_groups(self, search_key=None, search_value=None,
+ flags=0)
+
+ Usage:
+ Return a list of objects of lsm.AccessGroup.
+ If 'search_key' and 'search_value' is defined, only access group
+ matches will be contained in return list.
+ Valid search keys are stored in
+ lsm.AccessGroup.SUPPORTED_SEARCH_KEYS:
+ id # Search lsm.AccessGroup.id property
+ access_group_id # Search lsm.AccessGroup.id property
+ name # Search lsm.AccessGroup.name property
+ init_id # If given init_id is contained in
+ # lsm.AccessGroup.init_ids
+ volume_id # If access group is masked to given volume.
+ system_id # Return access group for given system only.
+ When no matches found, return a empty list
+ Parameters:
+ search_key # String. The key name for the search
+ search_value # The value of search_key to match
+ flags # Integer, Bit Map(Check Appendix.D for detail).
+ # Valid value:
+ # lsm.AccessGroup.FLAG_RETRIEVE_OPTIONAL_DATA
+ # # Return lsm.AccessGroup object will
+ # # contain optional properties.
+ # # But currently, there is no optional
+ # # property for lsm.AccessGroup yet, this
+ # # will make no difference.
+ Returns:
+ [lsm.AccessGroup,] # A list of lsm.AccessGroup objects.
+ or
+ [] # Nothing found.
+ Exception:
+ LsmError
+ lsm.ErrorNumber.PLUGIN_ERROR
+ # Plugin error when query.
+ lsm.ErrorNumber.AG_QUERY_INVALID_SEARCH_KEY
+ # Got invalid search key. Should be one of
+ # lsm.AccessGroup.SUPPORTED_SEARCH_KEYS
+ Sample:
+ lsm_cli_obj = lsm.Client('sim://')
+ access_groups = lsm_cli_obj.access_groups(
+ search_key=None, search_value=None, flags=0)
+ Capability:
+ lsm.Capabilities.ACCESS_GROUPS
+ # Indicate current systems support access group query via
+ # lsm.Client.access_groups() method.
+ lsm.Capabilities.ACCESS_GROUPS_QUICK_SEARCH
+ # This capability indicate plugin can perform a quick
+ # search without pull all data from storage system.
+ # Without this capability, plugin will pull all data from
+ # storage system to search given key. It only save socket data
+ # transfer between plugin and user API. If user want to
+ # do multiple search, it is suggested to query all access
+ # groups without 'search_key' parameter, then filter in user's
+ # code space.
+ """
+ if search_key and search_key not in AccessGroup.SUPPORTED_SEARCH_KEYS:
+ raise LsmError(ErrorNumber.AG_QUERY_INVALID_SEARCH_KEY,
+ "Invalid search key %s" % search_key)
+
+ # Just save plugin's code lines.
+ if search_key and search_key == 'access_group_id':
+ search_key = 'id'
+
+ return self._tp.rpc('access_groups',
+ {"search_key":search_key,
+ "search_value":search_value,
+ "flags":flags})
+
+ ## Creates an access group with the specified initiators in it.
# @param self The this pointer
- # @param name The initiator group name
- # @param initiator_id Initiator id
- # @param id_type Type of initiator (Enumeration)
+ # @param access_group_name The access group name
+ # @param init_ids List, Initiator ids
+ # @param init_type Type of initiator (Enumeration)
# @param system_id Which system to create this group on
# @param flags Reserved for future use, must be zero.
# @returns AccessGroup on success, else raises LsmError
@_return_requires(AccessGroup)
- def access_group_create(self, name, initiator_id, id_type, system_id,
- flags=0):
- """
- Creates an access group and add the specified initiator id, id_type and
- desired access.
+ def access_group_create(self, access_group_name, init_ids, init_type,
+ system_id, flags=0):
+ """
+ lsm.Client.access_group_create(self, access_group_name, init_ids,
+ init_type, system_ids, flags=0)
+
+ Usage:
+ Create a access group.
+ Parameters:
+ access_group_name # String. The name for new access group.
+ # If provided access_group_name does not meet
+ # the requirement of storage system, storage
+ # system will chose a valid one in stead of
+ # raise a error.
+ init_ids # List of string. The initiator IDs.
+ init_type # Integer. The type of initiator, could be one
+ # of these values:
+ # * lsm.AccessGroup.INIT_TYPE_WWPN
+ # * lsm.AccessGroup.INIT_TYPE_WWNN
+ # * lsm.AccessGroup.INIT_TYPE_HOSTNAME
+ # * lsm.AccessGroup.INIT_TYPE_ISCSI_IQN
+ # * lsm.AccessGroup.INIT_TYPE_SAS
+ system_id # The id of lsm.System where this access group
+ # should create on.
+ flags # Reserved for future use, should be 0.
+ Returns:
+ lsm.AccessGroup # Object of lsm.AccessGroup
+ Exception:
+ LsmError
+ lsm.ErrorNumber.PLUGIN_ERROR
+ # Plugin error when query. Please report a bug.
+ lsm.ErrorNumber.AG_CREATE_NO_SUPPORT
+ # Does not support creating access group.
+ lsm.ErrorNumber.AG_CREATE_INVALID_INIT_ID
+ # Provide init_ids contain ilegal string. Exmaple:
+ # WWPN does not in (?:[0-9a-f]{2}:){7}[0-9a-f]{2} format.
+ # Its user's responsibity for convering init_ids into lsm
+ # requested format.
+ lsm.ErrorNumber.AG_CREATE_INVALID_INIT_TYPE
+ # Got one of these ilegal init_type:
+ # * lsm.AccessGroup.INIT_TYPE_OTHER
+ # * lsm.AccessGroup.INIT_TYPE_UNKNOWN
+ lsm.ErrorNumber.AG_CREATE_NO_SUPPORT_WWPN
+ # System does not support WWPN initiator type.
+ lsm.ErrorNumber.AG_CREATE_NO_SUPPORT_WWNN
+ lsm.ErrorNumber.AG_CREATE_NO_SUPPORT_HOSTNAME
+ lsm.ErrorNumber.AG_CREATE_NO_SUPPORT_ISCSI_IQN
+ lsm.ErrorNumber.AG_CREATE_NO_SUPPORT_SAS
+ lsm.ErrorNumber.AG_CREATE_EXCEED_LIMITATION_INIT_COUNT
+ # System is reaching limitation of access group initator
+ # count. Example, if a system only allow up to 10
+ # initiators in one access group, if user provides 11 or
+ # more initiators, this error will be raised.
+ lsm.ErrorNumber.AG_CREATE_EXCEED_LIMITATION_AG_COUNT
+ # System is reaching limitation of total access group
+ # counts. Example, if a system only allow 100 access
+ # groups in total, when trying to create the 101 one, this
+ # error will be raised.
+ Sample:
+ lsm_cli_obj = lsm.Client('sim://')
+ new_ag = lsm_cli_obj.access_group_create(
+ "Test access group",
+ ['20:00:00:1b:21:67:65:d7', '20:00:00:1b:21:67:65:d6'],
+ lsm.AccessGroup.INIT_TYPE_WWPN)
+ Capability:
+ lsm.Capabilities.ACCESS_GROUP_CREATE
+ # Indicate current systems support create access group via
+ # lsm.Client.access_group_create() method.
+ lsm.Capabilities.ACCESS_GROUP_CREATE_WWPN
+ # Support creating WWPN access group.
+ lsm.Capabilities.ACCESS_GROUP_CREATE_WWNN
+ lsm.Capabilities.ACCESS_GROUP_CREATE_HOSTNAME
+ lsm.Capabilities.ACCESS_GROUP_CREATE_ISCSI_IQN
+ lsm.Capabilities.ACCESS_GROUP_CREATE_SAS
"""
return self._tp.rpc('access_group_create', _del_self(locals()))
## Deletes an access group.
- # @param self The this pointer
- # @param group The access group to delete
- # @param flags Reserved for future use, must be zero.
- # @returns None on success, throws LsmError on errors.
- @_return_requires(None)
- def access_group_del(self, group, flags=0):
- """
- Deletes an access group
- """
- return self._tp.rpc('access_group_del', _del_self(locals()))
-
- ## Adds an initiator to an access group
- # @param self The this pointer
- # @param group Group to add initiator to
- # @param initiator_id Initiators id
- # @param id_type Initiator id type (enumeration)
- # @param flags Reserved for future use, must be zero.
- # @returns None on success, throws LsmError on errors.
- @_return_requires(None)
- def access_group_add_initiator(self, group, initiator_id, id_type,
- flags=0):
- """
- Adds an initiator to an access group
- """
- return self._tp.rpc('access_group_add_initiator', _del_self(locals()))
-
- ## Deletes an initiator from an access group
# @param self The this pointer
- # @param group The access group to remove initiator from
- # @param initiator_id The initiator to remove from the group
+ # @param access_group The lsm.AccessGroup to delete
# @param flags Reserved for future use, must be zero.
# @returns None on success, throws LsmError on errors.
@_return_requires(None)
- def access_group_del_initiator(self, group, initiator_id, flags=0):
- """
- Deletes an initiator from an access group
- """
- return self._tp.rpc('access_group_del_initiator', _del_self(locals()))
-
- ## Returns the list of volumes that access group has access to.
- # @param self The this pointer
- # @param group The access group to list volumes for
- # @param flags Reserved for future use, must be zero.
- # @returns list of volumes
- @_return_requires([Volume])
- def volumes_accessible_by_access_group(self, group, flags=0):
- """
- Returns the list of volumes that access group has access to.
- """
- return self._tp.rpc('volumes_accessible_by_access_group',
- _del_self(locals()))
-
- ##Returns the list of access groups that have access to the specified
- #volume.
- # @param self The this pointer
- # @param volume The volume to list access groups for
- # @param flags Reserved for future use, must be zero.
- # @returns list of access groups
- @_return_requires([AccessGroup])
- def access_groups_granted_to_volume(self, volume, flags=0):
- """
- Returns the list of access groups that have access to the specified
- volume.
- """
- return self._tp.rpc('access_groups_granted_to_volume',
- _del_self(locals()))
+ def access_group_delete(self, access_group, flags=0):
+ """
+ lsm.Client.access_group_delete(self, access_group, flags=0)
+
+ Usage:
+ Delete a access group. Only access group has no volume masked to
+ can be deleted.
+ User can use this method to query volumes masked to give access
+ group:
+ lsm.Client.volumes(self, search_key='access_group_id',
+ search_value=access_group.id)
+ Parameters:
+ access_group # Object of lsm.AccessGroup to delete.
+ flags # Reserved for future use, should be 0.
+ Returns:
+ None # Access group deleted without errors.
+ Exception:
+ LsmError
+ lsm.ErrorNumber.PLUGIN_ERROR
+ # Plugin error when query. Please report a bug.
+ lsm.ErrorNumber.AG_DELETE_NO_SUPPORT
+ # Does not support deleting a access group.
+ lsm.ErrorNumber.AG_DELETE_IS_MASKED
+ # Access group has volume masked to.
+ Sample:
+ lsm_cli_obj = lsm.Client('sim://')
+ new_ag = lsm_cli_obj.access_group_create(
+ "Test access group",
+ ['20:00:00:1b:21:67:65:d7', '20:00:00:1b:21:67:65:d6'],
+ lsm.AccessGroup.INIT_TYPE_WWPN)
+ lsm_cli_obj.access_group_delete(new_ag)
+ Capability:
+ lsm.Capabilities.ACCESS_GROUP_DELETE
+ # Indicate current systems support delete access group via
+ # lsm.Client.access_group_delete() method.
+ """
+ return self._tp.rpc('access_group_delete', _del_self(locals()))
+
+ ## Update an existing access group with new initiators.
+ # @param self The this pointer
+ # @param access_group The lsm.AccessGroup object
+ # @param new_init_ids New initiator IDs
+ # @param flags Reserved for future use, must be zero.
+ # @returns AccessGroup on success, else raises LsmError
+ @_return_requires(AccessGroup)
+ def access_group_edit(self, access_group, new_init_ids, flags=0):
+ """
+ lsm.Client.access_group_edit(self, access_group, new_init_ids, flags=0)
+
+ Usage:
+ Change the initiators of certain access group.
+ Parameters:
+ access_group # Object of lsm.AccessGroup to edit.
+ new_init_ids # List of string. New initiator IDs. Old one will
+ # be overided.
+ flags # Reserved for future use, should be 0.
+ Returns:
+ None # Access group edited without errors.
+ Exception:
+ LsmError
+ lsm.ErrorNumber.PLUGIN_ERROR
+ # Plugin error when query. Please report a bug.
+ lsm.ErrorNumber.AG_EDIT_NO_SUPPORT
+ # Does not support deleting a access group.
+ lsm.ErrorNumber.AG_EDIT_INVALID_INIT_ID
+ # Provide new_init_ids contain ilegal string.
+ Sample:
+ lsm_cli_obj = lsm.Client('sim://')
+ new_ag = lsm_cli_obj.access_group_create(
+ "Test access group",
+ ['20:00:00:1b:21:67:65:d7', '20:00:00:1b:21:67:65:d6'],
+ lsm.AccessGroup.INIT_TYPE_WWPN)
+ # Remove one WWPN out.
+ lsm_cli_obj.access_group_edit(new_ag, ['20:00:00:1b:21:67:65:d7'])
+ Capability:
+ lsm.Capabilities.ACCESS_GROUP_EDIT
+ # Indicate current systems support edit access group member
+ # via lsm.Client.access_group_edit() method.
+ """
+ return self._tp.rpc('access_group_edit', _del_self(locals()))
## Checks to see if a volume has child dependencies.
# @param self The this pointer
diff --git a/lsm/lsm/_common.py b/lsm/lsm/_common.py
index 19b0a24..fbf9c67 100644
--- a/lsm/lsm/_common.py
+++ b/lsm/lsm/_common.py
@@ -503,6 +503,51 @@ class ErrorNumber(object):
DISK_BUSY = 500
VOLUME_BUSY = 501
+ AG_QUERY_INVALID_SEARCH_KEY = 600
+ # Reserve 60x for AG_QUERY_XXX
+
+ AG_CREATE_NO_SUPPORT = 611
+ AG_CREATE_INVALID_INIT_ID = 612
+ AG_CREATE_INVALID_INIT_TYPE = 613
+ AG_CREATE_NO_SUPPORT_WWPN = 614
+ AG_CREATE_NO_SUPPORT_WWNN = 615
+ AG_CREATE_NO_SUPPORT_HOSTNAME = 616
+ AG_CREATE_NO_SUPPORT_ISCSI_IQN = 617
+ AG_CREATE_NO_SUPPORT_SAS = 618
+ AG_CREATE_EXCEED_LIMITATION_INIT_COUNT = 619
+ AG_CREATE_EXCEED_LIMITATION_AG_COUNT = 610
+ # Reserve 62x for AG_CREATE_XXX
+
+ AG_DELETE_NO_SUPPORT = 630
+ AG_DELETE_IS_MASKED = 631
+ # Reserve 63x for AG_DELETE_XXX
+
+ AG_EDIT_NO_SUPPORT = 640
+ AG_EDIT_INVALID_INIT_ID = 641
+ # Reserve 64x for AG_EDIT_XXX
+
+ MASK_QUERY_INVALID_SEARCH_KEY = 650
+ # Reserve 65x for MASK_QUERY_XXX
+
+ VOL_MASK_NO_SUPPORT = 660
+ VOL_MASK_INVALID_VOLUME = 661
+ VOL_MASK_INVALID_ACCESS_GROUP = 662
+ VOL_MASK_NO_SUPPORT_ACCESS_TYPE_RW = 663
+ VOL_MASK_NO_SUPPORT_ACCESS_TYPE_RO = 664
+ VOL_MASK_NOT_ALLOW_ACCESS_TYPE_RW = 665
+ VOL_MASK_NOT_ALLOW_ACCESS_TYPE_RO = 666
+ # Reserve 66x for MASK_QUERY_XXX
+ # Reserve 67x for MASK_QUERY_XXX
+
+ VOL_UNMASK_INVALID_PARAMETER = 680
+ VOL_UNMASK_INVALID_MASK_INFO = 681
+ VOL_UNMASK_INVALID_VOLUME = 682
+ VOL_UNMASK_INVALID_ACCESS_GROUP = 683
+ VOL_UNMASK_NO_SUPPORT = 684
+ VOL_UNMASK_CONFLICT_PARAMETERS = 685
+ VOL_UNMASK_MISSING_VOLUME = 686
+ VOL_UNMASK_MISSING_ACCESS_GROUP = 687
+
class JobStatus(object):
INPROGRESS = 1
diff --git a/lsm/lsm/_data.py b/lsm/lsm/_data.py
index 603bf16..219f3bd 100644
--- a/lsm/lsm/_data.py
+++ b/lsm/lsm/_data.py
@@ -263,48 +263,6 @@ class IData(object):
@default_property('id', doc="Unique identifier")
-@default_property('type', doc="Enumerated initiator type")
-@default_property('name', doc="User supplied name")
-class Initiator(IData):
- """
- Represents an initiator.
- """
- (TYPE_OTHER, TYPE_PORT_WWN, TYPE_NODE_WWN, TYPE_HOSTNAME, TYPE_ISCSI,
- TYPE_SAS) = (1, 2, 3, 4, 5, 7)
-
- _type_map = {1: 'Other', 2: 'Port WWN', 3: 'Node WWN', 4: 'Hostname',
- 5: 'iSCSI', 7: "SAS"}
-
- _MAN_PROPERTIES_2_HEADER = {
- 'id': 'ID',
- 'name': 'Name',
- 'type': 'Type',
- }
-
- _MAN_PROPERTIES_SEQUENCE = ['id', 'name', 'type']
-
- def _value_convert(self, key_name, value, human, enum_as_number,
- list_convert):
- if not enum_as_number:
- if key_name == 'type':
- value = Initiator._type_to_str(value)
- return value
-
- @staticmethod
- def _type_to_str(init_type):
- return Initiator._type_map[init_type]
-
- def __init__(self, _id, _type, _name):
-
- if not _name or not len(_name):
- name = "Unsupported"
-
- self._id = _id # Identifier
- self._type = _type # Initiator type id
- self._name = _name # Initiator name
-
-
-@default_property('id', doc="Unique identifier")
@default_property('name', doc="Disk name (aka. vendor)")
@default_property('disk_type', doc="Enumerated type of disk")
@default_property('block_size', doc="Size of each block")
@@ -1404,31 +1362,48 @@ class BlockRange(IData):
@default_property('id', doc="Unique instance identifier")
@default_property('name', doc="Access group name")
-@default_property('initiators', doc="List of initiators")
+@default_property('init_ids', doc="List of initiators")
+@default_property('init_type', doc="Initiator type")
@default_property('system_id', doc="System identifier")
+@default_property("optional_data", doc="Optional data")
class AccessGroup(IData):
- def __init__(self, _id, _name, _initiators, _system_id='NA'):
- self._id = _id
- self._name = _name # AccessGroup name
- self._initiators = _initiators # List of initiators
- self._system_id = _system_id # System id this group belongs
- _MAN_PROPERTIES_2_HEADER = {
- 'id': 'ID',
- 'name': 'Name',
- 'initiators': 'Initiator IDs',
- 'system_id': 'System ID',
- }
+ FLAG_RETRIEVE_FULL_INFO = 1 << 0
- _MAN_PROPERTIES_SEQUENCE = ['id', 'name', 'initiators', 'system_id']
- _OPT_PROPERTIES_SEQUENCE = []
+ SUPPORTED_SEARCH_KEYS = ['id', 'access_group_id', 'name', 'init_ids',
+ 'volume_id', 'system_id']
- def _value_convert(self, key_name, value, human, enum_as_number,
- list_convert):
- if list_convert:
- if key_name == 'initiators':
- value = ','.join(str(x) for x in value)
- return value
+ INIT_TYPE_UNKNOWN = 0
+ INIT_TYPE_OTHER = 1
+ INIT_TYPE_WWPN = 2
+ INIT_TYPE_WWNN = 3
+ INIT_TYPE_HOSTNAME = 4
+ INIT_TYPE_ISCSI_IQN = 5
+ INIT_TYPE_SAS = 6
+
+ OPT_PROPERTIES = []
+
+ def __init__(self, _id, _name, _init_ids, _init_type, _system_id,
+ _optional_data=None):
+ self._id = _id
+ self._name = _name # AccessGroup name
+ self._init_ids = _init_ids # List of initiators
+ self._init_type = _init_type # Initiator type
+ self._system_id = _system_id # System id this group belongs
+
+ if _optional_data is None:
+ self._optional_data = OptionalData()
+ else:
+ #Make sure the properties only contain ones we permit
+ allowed = set(AccessGroup.OPT_PROPERTIES)
+ actual = set(_optional_data.list())
+
+ if actual <= allowed:
+ self._optional_data = _optional_data
+ else:
+ raise LsmError(ErrorNumber.INVALID_ARGUMENT,
+ "Property keys are invalid: %s" %
+ "".join(actual - allowed))
class OptionalData(IData):
@@ -1492,30 +1467,30 @@ class Capabilities(IData):
VOLUME_ONLINE = 34
VOLUME_OFFLINE = 35
- ACCESS_GROUP_GRANT = 36
- ACCESS_GROUP_REVOKE = 37
- ACCESS_GROUP_LIST = 38
- ACCESS_GROUP_CREATE = 39
- ACCESS_GROUP_DELETE = 40
- ACCESS_GROUP_ADD_INITIATOR = 41
- ACCESS_GROUP_DEL_INITIATOR = 42
+ ACCESS_GROUPS = 36
+ ACCESS_GROUPS_QUICK_SEARCH = 37
+ ACCESS_GROUP_CREATE = 38
+ ACCESS_GROUP_DELETE = 39
+ ACCESS_GROUP_EDIT = 39
- VOLUMES_ACCESSIBLE_BY_ACCESS_GROUP = 43
- ACCESS_GROUPS_GRANTED_TO_VOLUME = 44
+ ACCESS_GROUP_CREATE_WWPN = 40
+ ACCESS_GROUP_CREATE_WWNN = 41
+ ACCESS_GROUP_CREATE_HOSTNAME = 42
+ ACCESS_GROUP_CREATE_ISCSI_IQ = 43
+ ACCESS_GROUP_CREATE_SAS = 44
VOLUME_CHILD_DEPENDENCY = 45
VOLUME_CHILD_DEPENDENCY_RM = 46
- INITIATORS = 47
- INITIATORS_GRANTED_TO_VOLUME = 48
-
- VOLUME_INITIATOR_GRANT = 50
- VOLUME_INITIATOR_REVOKE = 51
- VOLUME_ACCESSIBLE_BY_INITIATOR = 52
VOLUME_ISCSI_CHAP_AUTHENTICATION = 53
VOLUME_THIN = 55
+ MASK_INFOS = 60
+ MASK_INFOS_QUICK_SEARCH = 61
+ VOLUME_MASK = 62
+ VOLUME_UNMASK = 63
+
#File system
FS = 100
FS_DELETE = 101
@@ -1622,6 +1597,45 @@ class PlugData(IData):
self.desc = description
self.version = plugin_version
+@default_property('id', doc="Unique Identifier")
+@default_property('volume_id', doc="Volume ID")
+@default_property('access_group_id', doc="Access Group ID")
+@default_property('access_type', doc="Access Type")
+@default_property('system_id', doc="System ID")
+@default_property("optional_data", doc="Optional data")
+class MaskInfo(IData):
+
+ ACCESS_TYPE_UNKNOWN = 0
+ ACCESS_TYPE_READ_WRITE = 1
+ ACCESS_TYPE_READ_ONLY = 2
+
+ OPT_PROPERTIES = []
+
+ SUPPORTED_SEARCH_KEYS = ['id', 'volume_id', 'access_group_id',
+ 'system_id']
+
+ def __init__(self, _id, _volume_id, _access_group_id, _access_type,
+ _system_id, _optional_data=None):
+ self._id = _id
+ self._volume_id = _volume_id
+ self._access_group_id = _access_group_id
+ self._access_type = _access_type
+ self._system_id = _system_id
+
+ if _optional_data is None:
+ self._optional_data = OptionalData()
+ else:
+ #Make sure the properties only contain ones we permit
+ allowed = set(MaskInfo.OPT_PROPERTIES)
+ actual = set(_optional_data.list())
+
+ if actual <= allowed:
+ self._optional_data = _optional_data
+ else:
+ raise LsmError(ErrorNumber.INVALID_ARGUMENT,
+ "Property keys are invalid: %s" %
+ "".join(actual - allowed))
+
if __name__ == '__main__':
#TODO Need some unit tests that encode/decode all the types with nested
--
1.8.3.1