Discussion:
[Libstoragemgmt-devel] [PATCH 0/3] New lsm.TargetPort class.
Gris Ge
2014-06-19 16:15:32 UTC
Permalink
* TargetPort is representing the target port of storage array where
host or switch is connected.

* This patch set is based on 2114cdbcafc19d161ab3ef2176b43fec03b1da35
The simulator version constant might need change as other patch already
posted with version bump. Please feel free to amend.

Gris Ge (3):
Python libraray: New lsm.TargetPort class
lsmcli: Support query TargetPort
Simulator Plugin: Add lsm.TargetPort support

doc/man/lsmcli.1.in | 6 ++++-
plugin/sim/simarray.py | 58 ++++++++++++++++++++++++++++++++++++++++--
plugin/sim/simulator.py | 8 ++++++
python_binding/lsm/__init__.py | 2 +-
python_binding/lsm/_client.py | 6 ++++-
python_binding/lsm/_data.py | 53 ++++++++++++++++++++++++++++++++++++++
python_binding/lsm/_iplugin.py | 3 +++
tools/lsmcli/cmdline.py | 23 +++++++++++++++--
tools/lsmcli/data_display.py | 44 +++++++++++++++++++++++++++++++-
9 files changed, 195 insertions(+), 8 deletions(-)
--
1.8.3.1
Gris Ge
2014-06-19 16:15:33 UTC
Permalink
lsm.TargetPort
* id
* port_type
FCoE
FC
iSCSI
* service_address
# The address used by upper layer like FC and iSCSI:
# FC/FCoE: WWPN
# iSCSI: IQN
* network_address
# The address used by network layer like FC and TCP/IP:
# FC/FCoE: WWPN
# iSCSI: IPv4:Port
# [IPv6]:Port
* physical_address
# The address used by physical layer like FC-0 and MAC:
# FC: WWPN
# FCoE: MAC
# iSCSI: MAC
* physical_name
# The name of physical port. Administrator could use this name to
# locate the port on storage system.
* system_id
Methods:
lsm.Client.target_ports(self, search_key=None, search_value=None, flags=0)


Signed-off-by: Gris Ge <***@redhat.com>
---
python_binding/lsm/__init__.py | 2 +-
python_binding/lsm/_client.py | 6 ++++-
python_binding/lsm/_data.py | 53 ++++++++++++++++++++++++++++++++++++++++++
python_binding/lsm/_iplugin.py | 3 +++
4 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/python_binding/lsm/__init__.py b/python_binding/lsm/__init__.py
index b1f059b..7bd8a94 100644
--- a/python_binding/lsm/__init__.py
+++ b/python_binding/lsm/__init__.py
@@ -6,7 +6,7 @@ from _common import Error, Info, LsmError, ErrorLevel, ErrorNumber, \
JobStatus, uri_parse, md5, Proxy, size_bytes_2_size_human, \
common_urllib2_error_handler, size_human_2_size_bytes
from _data import (Disk, Volume, Pool, System, FileSystem, FsSnapshot,
- NfsExport, BlockRange, AccessGroup,
+ NfsExport, BlockRange, AccessGroup, TargetPort,
Capabilities, txt_a)
from _iplugin import IPlugin, IStorageAreaNetwork, INetworkAttachedStorage, \
INfs
diff --git a/python_binding/lsm/_client.py b/python_binding/lsm/_client.py
index 74e663c..2a1d729 100644
--- a/python_binding/lsm/_client.py
+++ b/python_binding/lsm/_client.py
@@ -21,7 +21,7 @@ import unittest
from lsm import (Volume, NfsExport, Capabilities, Pool, System,
Disk, AccessGroup, FileSystem, FsSnapshot,
uri_parse, LsmError, JobStatus, ErrorNumber,
- INetworkAttachedStorage)
+ INetworkAttachedStorage, TargetPort)

from _common import return_requires as _return_requires
from _common import UDS_PATH as _UDS_PATH
@@ -1062,6 +1062,10 @@ class Client(INetworkAttachedStorage):
"""
return self._tp.rpc('export_remove', _del_self(locals()))

+ @_return_requires([TargetPort])
+ def target_ports(self, search_key=None, search_value=None, flags=0):
+ return self._tp.rpc('target_ports', _del_self(locals()))
+

class _TestClient(unittest.TestCase):
def wait_to_finish(self, job, vol):
diff --git a/python_binding/lsm/_data.py b/python_binding/lsm/_data.py
index 04c7af3..ee298d7 100644
--- a/python_binding/lsm/_data.py
+++ b/python_binding/lsm/_data.py
@@ -691,6 +691,57 @@ class AccessGroup(IData):
self._plugin_data = _plugin_data


+@default_property('id', doc="Unique instance identifier")
+@default_property('port_type', doc="Target port type")
+@default_property('service_address', doc="Target port service address")
+@default_property('network_address', doc="Target port network address")
+@default_property('physical_address', doc="Target port physical address")
+@default_property('physical_name', doc="Target port physical port name")
+@default_property('system_id', doc="System identifier")
+@default_property('plugin_data', doc="Plugin private data")
+class TargetPort(IData):
+ SUPPORTED_SEARCH_KEYS = ['id', 'system_id']
+
+ PORT_TYPE_UNKNOWN = 0
+ PORT_TYPE_OTHER = 1
+ PORT_TYPE_FC = 2
+ PORT_TYPE_FCOE = 3
+ PORT_TYPE_ISCSI = 4
+
+ def __init__(self, _id, _port_type, _service_address,
+ _network_address, _physical_address, _physical_name,
+ _system_id, _plugin_data=None):
+ self._id = _id
+ self._port_type = _port_type
+ self._service_address = _service_address
+ # service_address:
+ # The address used by upper layer like FC and iSCSI:
+ # FC and FCoE: WWPN
+ # iSCSI: IQN
+ # String. Lower case, split with : every two digits if WWPN.
+ self._network_address = _network_address
+ # network_address:
+ # The address used by network layer like FC and TCP/IP:
+ # FC/FCoE: WWPN
+ # iSCSI: IPv4:Port
+ # [IPv6]:Port
+ # String. Lower case, split with : every two digits if WWPN.
+ self._physical_address = _physical_address
+ # physical_address:
+ # The address used by physical layer like FC-0 and MAC:
+ # FC: WWPN
+ # FCoE: MAC
+ # iSCSI: MAC
+ # String. Lower case, split with : every two digits.
+ self._physical_name = _physical_name
+ # physical_name
+ # The name of physical port. Administrator could use this name to
+ # locate the port on storage system.
+ # String.
+ self._system_id = _system_id
+ self._plugin_data = _plugin_data
+
+
class Capabilities(IData):
(
UNSUPPORTED, # Not supported
@@ -812,6 +863,8 @@ class Capabilities(IData):
ACCESS_GROUPS_QUICK_SEARCH = 213
FS_QUICK_SEARCH = 214
NFS_EXPORTS_QUICK_SEARCH = 215
+ TARGET_PORTS = 215
+ TARGET_PORTS_QUICK_SEARCH = 217

def _to_dict(self):
rc = {'class': self.__class__.__name__,
diff --git a/python_binding/lsm/_iplugin.py b/python_binding/lsm/_iplugin.py
index d903fc3..fed9bba 100644
--- a/python_binding/lsm/_iplugin.py
+++ b/python_binding/lsm/_iplugin.py
@@ -359,6 +359,9 @@ class IStorageAreaNetwork(IPlugin):
"""
raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")

+ def target_ports(self, search_key=None, search_value=None, flags=0):
+ raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")
+
class INetworkAttachedStorage(IPlugin):
"""
Class the represents Network attached storage (Common NFS/CIFS operations)
--
1.8.3.1
Gris Ge
2014-06-19 16:15:34 UTC
Permalink
* New command to query target ports:
lsmcli list --type TARGET_PORTS [ --tgt <TGT_ID> ]
* Manpage updated.

Signed-off-by: Gris Ge <***@redhat.com>
---
doc/man/lsmcli.1.in | 6 +++++-
tools/lsmcli/cmdline.py | 23 +++++++++++++++++++++--
tools/lsmcli/data_display.py | 44 +++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 69 insertions(+), 4 deletions(-)

diff --git a/doc/man/lsmcli.1.in b/doc/man/lsmcli.1.in
index b22555c..a93788e 100644
--- a/doc/man/lsmcli.1.in
+++ b/doc/man/lsmcli.1.in
@@ -157,7 +157,7 @@ Required. Valid values are:
\fBEXPORTS\fR, \fBDISKS\fR,
.br
\fBNFS_CLIENT_AUTH\fR, \fBACCESS_GROUPS\fR,
-\fBSYSTEMS\fR, \fBPLUGINS.
+\fBSYSTEMS\fR, \fBTARGET_PORTS\fR, \fBPLUGINS\fR.
.TP
\fB--fs\fR \fI<FS_ID>\fR
Required for \fB--type\fR=\fBSNAPSHOTS\fR. List the snapshots of certain
@@ -198,6 +198,10 @@ of resources: \fBFS\fR.
\fB--nfs-export\fR \fI<NFS_EXPORT_ID>\fR
Search resources from NFS export with NFS_EXPORT_ID. Only supported by these
types of resources: \fBEXPORTS\fR.
+.TP
+\fB--tgt\fR \fI<TGT_ID>\fR
+Search resources from target port with target port ID. Only supported by these
+types of resources: \fBTARGET_PORTS\fR.

.SS job-status
Retrieve information about a job.
diff --git a/tools/lsmcli/cmdline.py b/tools/lsmcli/cmdline.py
index 97aed06..a637f04 100644
--- a/tools/lsmcli/cmdline.py
+++ b/tools/lsmcli/cmdline.py
@@ -29,7 +29,7 @@ from argparse import RawTextHelpFormatter
from lsm import (Client, Pool, VERSION, LsmError, Capabilities, Disk,
Volume, JobStatus, ErrorNumber, BlockRange,
uri_parse, Proxy, size_human_2_size_bytes,
- AccessGroup, FileSystem, NfsExport)
+ AccessGroup, FileSystem, NfsExport, TargetPort)

from lsm.lsmcli.data_display import (
DisplayData, PlugData, out,
@@ -101,7 +101,7 @@ def _get_item(l, the_id, friendly_name='item', raise_error=True):

list_choices = ['VOLUMES', 'POOLS', 'FS', 'SNAPSHOTS',
'EXPORTS', "NFS_CLIENT_AUTH", 'ACCESS_GROUPS',
- 'SYSTEMS', 'DISKS', 'PLUGINS']
+ 'SYSTEMS', 'DISKS', 'PLUGINS', 'TARGET_PORTS',]

init_types = ('WWPN', 'WWNN', 'ISCSI', 'HOSTNAME', 'SAS')
init_id_help = "Access Group Initiator type: " + \
@@ -173,6 +173,8 @@ size_opt = dict(name='--size', metavar='<SIZE>', help=size_help)
init_type_opt = dict(name="--init-type", help=init_id_help,
metavar='<INIT_TYPE>', choices=init_types,
type=str.upper)
+tgt_id_opt = dict(name="--tgt", help="Search by target port ID",
+ metavar='<TGT_ID>')

cmds = (
dict(
@@ -195,6 +197,7 @@ cmds = (
dict(ag_id_filter_opt),
dict(fs_id_opt),
dict(nfs_export_id_filter_opt),
+ dict(tgt_id_opt),
],
),

@@ -884,6 +887,9 @@ class CmdLine:
if args.nfs_export:
search_key = 'nfs_export_id'
search_value = args.nfs_export
+ if args.tgt:
+ search_key = 'tgt_port_id'
+ search_value = args.tgt

if args.type == 'VOLUMES':
if search_key == 'volume_id':
@@ -960,6 +966,15 @@ class CmdLine:
"disk listing" % search_key)
self.display_data(
self.c.disks(search_key, search_value))
+ elif args.type == 'TARGET_PORTS':
+ if search_key == 'tgt_port_id':
+ search_key = 'id'
+ if search_key and \
+ search_key not in TargetPort.SUPPORTED_SEARCH_KEYS:
+ raise ArgError("Search key '%s' is not supported by "
+ "target port listing" % search_key)
+ self.display_data(
+ self.c.target_ports(search_key, search_value))
elif args.type == 'PLUGINS':
self.display_available_plugins()
else:
@@ -1191,6 +1206,10 @@ class CmdLine:
cap.supported(Capabilities.ACCESS_GROUPS_QUICK_SEARCH))
self._cp("NFS_EXPORTS_QUICK_SEARCH",
cap.supported(Capabilities.NFS_EXPORTS_QUICK_SEARCH))
+ self._cp("TARGET_PORTS",
+ cap.supported(Capabilities.TARGET_PORTS))
+ self._cp("TARGET_PORTS_QUICK_SEARCH",
+ cap.supported(Capabilities.TARGET_PORTS_QUICK_SEARCH))

def plugin_info(self, args):
desc, version = self.c.plugin_info()
diff --git a/tools/lsmcli/data_display.py b/tools/lsmcli/data_display.py
index 9c040a5..23ca148 100644
--- a/tools/lsmcli/data_display.py
+++ b/tools/lsmcli/data_display.py
@@ -21,7 +21,7 @@ from datetime import datetime

from lsm import (size_bytes_2_size_human, LsmError, ErrorNumber,
System, Pool, Disk, Volume, AccessGroup,
- FileSystem, FsSnapshot, NfsExport)
+ FileSystem, FsSnapshot, NfsExport, TargetPort)

BIT_MAP_STRING_SPLITTER = ','

@@ -307,6 +307,19 @@ def ag_init_type_str_to_lsm(init_type_str):
return _str_to_enum(init_type_str, _AG_INIT_TYPE_CONV)


+_TGT_PORT_TYPE_CONV = {
+ TargetPort.PORT_TYPE_UNKNOWN: 'Unknown',
+ TargetPort.PORT_TYPE_OTHER: 'Other',
+ TargetPort.PORT_TYPE_FC: 'FC',
+ TargetPort.PORT_TYPE_FCOE: 'FCoE',
+ TargetPort.PORT_TYPE_ISCSI: 'iSCSI',
+}
+
+
+def tgt_port_type_to_str(port_type):
+ return _enum_type_to_str(port_type, _TGT_PORT_TYPE_CONV)
+
+
class PlugData(object):
def __init__(self, description, plugin_version):
self.desc = description
@@ -563,6 +576,35 @@ class DisplayData(object):
'value_conv_human': NFS_EXPORT_VALUE_CONV_HUMAN,
}

+ # lsm.TargetPort
+ TGT_PORT_MAN_HEADER = OrderedDict()
+ TGT_PORT_MAN_HEADER['id'] = 'ID'
+ TGT_PORT_MAN_HEADER['port_type'] = 'Type'
+ TGT_PORT_MAN_HEADER['physical_name'] = 'Physical Name'
+ TGT_PORT_MAN_HEADER['service_address'] = 'Address'
+ TGT_PORT_MAN_HEADER['network_address'] = 'Network Address'
+ TGT_PORT_MAN_HEADER['physical_address'] = 'Physical Address'
+ TGT_PORT_MAN_HEADER['system_id'] = 'System ID'
+
+ TGT_PORT_COLUMN_KEYS = []
+ for key_name in TGT_PORT_MAN_HEADER.keys():
+ # Skip these keys for column display
+ if key_name not in ['physical_address', 'network_address']:
+ TGT_PORT_COLUMN_KEYS.extend([key_name])
+
+ TGT_PORT_VALUE_CONV_ENUM = {
+ 'port_type': tgt_port_type_to_str,
+ }
+
+ TGT_PORT_VALUE_CONV_HUMAN = []
+
+ VALUE_CONVERT[TargetPort] = {
+ 'mandatory_headers': TGT_PORT_MAN_HEADER,
+ 'column_keys': TGT_PORT_COLUMN_KEYS,
+ 'value_conv_enum': TGT_PORT_VALUE_CONV_ENUM,
+ 'value_conv_human': TGT_PORT_VALUE_CONV_HUMAN,
+ }
+
@staticmethod
def _get_man_pro_value(obj, key, value_conv_enum, value_conv_human,
flag_human, flag_enum):
--
1.8.3.1
Gris Ge
2014-06-19 16:15:35 UTC
Permalink
* Add support of target port query.
* Version dump to 2.5

Signed-off-by: Gris Ge <***@redhat.com>
---
plugin/sim/simarray.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++--
plugin/sim/simulator.py | 8 +++++++
2 files changed, 64 insertions(+), 2 deletions(-)

diff --git a/plugin/sim/simarray.py b/plugin/sim/simarray.py
index 080e757..a1c0ac1 100644
--- a/plugin/sim/simarray.py
+++ b/plugin/sim/simarray.py
@@ -27,7 +27,7 @@ import time

from lsm import (size_human_2_size_bytes, size_bytes_2_size_human)
from lsm import (System, Volume, Disk, Pool, FileSystem, AccessGroup,
- FsSnapshot, NfsExport, md5, LsmError,
+ FsSnapshot, NfsExport, md5, LsmError, TargetPort,
ErrorNumber, JobStatus)

# Used for format width for disks
@@ -384,6 +384,18 @@ class SimArray(object):
return self.data.iscsi_chap_auth(init_id, in_user, in_pass, out_user,
out_pass, flags)

+ @staticmethod
+ def _sim_tgt_2_lsm(sim_tgt):
+ return TargetPort(
+ sim_tgt['tgt_id'], sim_tgt['port_type'],
+ sim_tgt['service_address'], sim_tgt['network_address'],
+ sim_tgt['physical_address'], sim_tgt['physical_name'],
+ sim_tgt['sys_id'])
+
+ def target_ports(self):
+ sim_tgts = self.data.target_ports()
+ return [SimArray._sim_tgt_2_lsm(t) for t in sim_tgts]
+

class SimData(object):
"""
@@ -494,7 +506,7 @@ class SimData(object):
}
"""
SIM_DATA_BLK_SIZE = 512
- SIM_DATA_VERSION = "2.4"
+ SIM_DATA_VERSION = "2.5"
SIM_DATA_SYS_ID = 'sim-01'
SIM_DATA_INIT_NAME = 'NULL'
SIM_DATA_TMO = 30000 # ms
@@ -659,6 +671,45 @@ class SimData(object):
'element_type': SimData.SIM_DATA_POOL_ELEMENT_TYPE,
}

+ self.tgt_dict = {
+ 'TGT_PORT_ID_01': {
+ 'tgt_id': 'TGT_PORT_ID_01',
+ 'port_type': TargetPort.PORT_TYPE_FC,
+ 'service_address': '50:0a:09:86:99:4b:8d:c5',
+ 'network_address': '50:0a:09:86:99:4b:8d:c5',
+ 'physical_address': '50:0a:09:86:99:4b:8d:c5',
+ 'physical_name': 'FC_a_0b',
+ 'sys_id': SimData.SIM_DATA_SYS_ID,
+ },
+ 'TGT_PORT_ID_02': {
+ 'tgt_id': 'TGT_PORT_ID_02',
+ 'port_type': TargetPort.PORT_TYPE_FCOE,
+ 'service_address': '50:0a:09:86:99:4b:8d:c6',
+ 'network_address': '50:0a:09:86:99:4b:8d:c6',
+ 'physical_address': '00:1b:21:3f:a1:b4',
+ 'physical_name': 'FCoE_b_0c',
+ 'sys_id': SimData.SIM_DATA_SYS_ID,
+ },
+ 'TGT_PORT_ID_03': {
+ 'tgt_id': 'TGT_PORT_ID_03',
+ 'port_type': TargetPort.PORT_TYPE_ISCSI,
+ 'service_address': 'iqn.1986-05.com.example:sim-tgt-03',
+ 'network_address': 'sim-iscsi-tgt-3.example.com:3260',
+ 'physical_address': 'a4:4e:31:47:f4:e0',
+ 'physical_name': 'iSCSI_c_0d',
+ 'sys_id': SimData.SIM_DATA_SYS_ID,
+ },
+ 'TGT_PORT_ID_04': {
+ 'tgt_id': 'TGT_PORT_ID_04',
+ 'port_type': TargetPort.PORT_TYPE_ISCSI,
+ 'service_address': 'iqn.1986-05.com.example:sim-tgt-03',
+ 'network_address': '10.0.0.1:3260',
+ 'physical_address': 'a4:4e:31:47:f4:e1',
+ 'physical_name': 'iSCSI_c_0e',
+ 'sys_id': SimData.SIM_DATA_SYS_ID,
+ },
+ }
+
return

def pool_free_space(self, pool_id):
@@ -1978,3 +2029,6 @@ class SimData(object):

del(self.pool_dict[pool_id])
return None
+
+ def target_ports(self):
+ return self.tgt_dict.values()
diff --git a/plugin/sim/simulator.py b/plugin/sim/simulator.py
index 45bed3c..eb8e367 100644
--- a/plugin/sim/simulator.py
+++ b/plugin/sim/simulator.py
@@ -84,6 +84,8 @@ class SimPlugin(INfs, IStorageAreaNetwork):
rc.set(Capabilities.ACCESS_GROUPS_QUICK_SEARCH,
Capabilities.UNSUPPORTED)
rc.set(Capabilities.NFS_EXPORTS_QUICK_SEARCH, Capabilities.UNSUPPORTED)
+ rc.set(Capabilities.TARGET_PORTS_QUICK_SEARCH,
+ Capabilities.UNSUPPORTED)
return rc

def plugin_info(self, flags=0):
@@ -307,3 +309,9 @@ class SimPlugin(INfs, IStorageAreaNetwork):

def export_remove(self, export, flags=0):
return self.sim_array.fs_unexport(export.id, flags)
+
+ def target_ports(self, search_key=None, search_value=None, flags=0):
+ sim_tgts = self.sim_array.target_ports()
+ return search_property(
+ [SimPlugin._sim_data_2_lsm(t) for t in sim_tgts],
+ search_key, search_value)
--
1.8.3.1
Loading...