Skip to content

Fibre Channel

This section documents the Fibre Channel functionality, which provides interfaces for working with Fibre Channel devices and host bus adapters.

sts.fc

Fibre Channel device management.

This module provides functionality for managing FC devices: - Host management - Remote port management - WWN handling - Device discovery - Transport type detection

Fibre Channel (FC) is a high-speed network technology used for: - Storage Area Networks (SAN) - Block-level data transfer - Enterprise storage connectivity

Key concepts: - WWN (World Wide Name): Unique device identifier - Host Bus Adapter (HBA): FC network interface - Ports: Physical or virtual FC connections - Zoning: Access control between devices

FcDevice dataclass

Bases: StorageDevice

Fibre Channel device representation.

A Fibre Channel device is identified by: - WWN (World Wide Name): Unique identifier - Host ID: Local HBA identifier - Remote ports: Connected target ports - Transport type: FC or FCoE

Device discovery uses: - sysfs entries (/sys/class/fc_*) - HBA driver information - SCSI subsystem links

Parameters:

Name Type Description Default
name str | None

Device name (optional, e.g. 'sda')

None
path Path | str | None

Device path (optional, defaults to /dev/)

None
size int | None

Device size in bytes (optional, discovered from device)

None
model str | None

Device model (optional)

None
wwn str | None

World Wide Name (optional, discovered from device)

None
host_id str | None

FC host ID (optional, discovered from device)

None
transport_type TransportType | None

Transport type (optional, discovered from device)

None
Example
device = FcDevice(name='sda')  # Discovers other values
device = FcDevice(wwn='10:00:5c:b9:01:c1:ec:71')  # Discovers device from WWN
Source code in sts_libs/src/sts/fc.py
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
@dataclass
class FcDevice(StorageDevice):
    """Fibre Channel device representation.

    A Fibre Channel device is identified by:
    - WWN (World Wide Name): Unique identifier
    - Host ID: Local HBA identifier
    - Remote ports: Connected target ports
    - Transport type: FC or FCoE

    Device discovery uses:
    - sysfs entries (/sys/class/fc_*)
    - HBA driver information
    - SCSI subsystem links

    Args:
        name: Device name (optional, e.g. 'sda')
        path: Device path (optional, defaults to /dev/<name>)
        size: Device size in bytes (optional, discovered from device)
        model: Device model (optional)
        wwn: World Wide Name (optional, discovered from device)
        host_id: FC host ID (optional, discovered from device)
        transport_type: Transport type (optional, discovered from device)

    Example:
        ```python
        device = FcDevice(name='sda')  # Discovers other values
        device = FcDevice(wwn='10:00:5c:b9:01:c1:ec:71')  # Discovers device from WWN
        ```
    """

    # Optional parameters from parent classes
    name: str | None = None
    path: Path | str | None = None
    size: int | None = None
    model: str | None = None

    # Optional parameters for this class
    wwn: str | None = None  # World Wide Name
    host_id: str | None = None  # Local HBA ID
    transport_type: TransportType | None = None  # FC or FCoE

    # Internal fields
    _remote_ports: list[str] = field(init=False, default_factory=list)

    # Sysfs paths for FC information
    HOST_PATH: ClassVar[Path] = Path('/sys/class/fc_host')
    REMOTE_PORT_PATH: ClassVar[Path] = Path('/sys/class/fc_remote_ports')

    def __post_init__(self) -> None:
        """Initialize FC device.

        Discovery process:
        1. Set device path if needed
        2. Find host ID from SCSI device link
        3. Get WWN from host information
        4. Determine transport type
        5. Validate WWN format

        Raises:
            DeviceError: If device cannot be initialized
        """
        # Set path based on name if not provided
        if not self.path and self.name:
            self.path = f'/dev/{self.name}'

        # Initialize parent class
        super().__post_init__()

        # Discover host_id from SCSI device link
        if not self.host_id and self.name:
            scsi_device_path = Path(f'/sys/block/{self.name}/device')
            if scsi_device_path.exists():
                # Extract host ID from symlink like:
                # /sys/devices/pci0000:00/0000:00:03.0/0000:08:00.0/host7/rport-7:0-0/target7:0:0/7:0:0:1
                match = re.search(r'host(\d+)', str(scsi_device_path.resolve()))
                if match:
                    self.host_id = match.group(1).strip()

        # Discover WWN from host information
        if not self.wwn and self.host_id:
            self.wwn = self.get_host_wwn(self.host_id)

        # Discover transport type from host
        if not self.transport_type and self.host_id:
            self.transport_type = self.get_host_transport_type(self.host_id)

        # Validate WWN format if provided
        if self.wwn and not self.is_valid_wwn(self.wwn):
            raise DeviceError(f'Invalid WWN format: {self.wwn}')

    @staticmethod
    def is_valid_wwn(wwn: str) -> bool:
        """Check if WWN is valid.

        WWN format: 8 pairs of hex digits separated by colons
        Example:
            ```python
            10:00:5c:b9:01:c1:ec:71

        Args:
                wwn: World Wide Name to check

        Returns:
                True if valid, False otherwise

        Example:
                FcDevice.is_valid_wwn('10:00:5c:b9:01:c1:ec:71')
                True
            ```
        """
        return bool(WWN_PATTERN.match(wwn.lower()))

    @staticmethod
    def standardize_wwn(wwn: str) -> str | None:
        """Standardize WWN format.

        Converts various WWN formats to standard format:
        - Removes '0x' prefix
        - Converts to lowercase
        - Adds colons between pairs
        - Validates final format

        Args:
            wwn: World Wide Name to standardize

        Returns:
            Standardized WWN or None if invalid

        Example:
            ```python
            FcDevice.standardize_wwn('500A0981894B8DC5')
            '50:0a:09:81:89:4b:8d:c5'
            ```
        """
        if not wwn:
            return None

        # Remove 0x and : characters
        wwn = re.sub('0x', '', wwn.lower()).replace(':', '')

        # Add : every 2 characters
        wwn = ':'.join(wwn[i : i + 2] for i in range(0, len(wwn), 2))

        return wwn if FcDevice.is_valid_wwn(wwn) else None

    @property
    def remote_ports(self) -> list[str]:
        """Get remote ports.

        Lists FC ports connected to this host:
        - Format: rport-H:B-R
          - H: Host number
          - B: Bus number
          - R: Remote port number

        Returns:
            List of remote port IDs

        Example:
            ```python
            device.remote_ports
            ['rport-0:0-1', 'rport-0:0-2']
            ```
        """
        if not self._remote_ports and self.host_id:
            result = run(f'ls {self.REMOTE_PORT_PATH} | grep rport-{self.host_id}')
            if result.succeeded:
                self._remote_ports = result.stdout.splitlines()
        return self._remote_ports

    def get_remote_port_wwn(self, port: str) -> str | None:
        """Get WWN of remote port.

        Reads port_name from sysfs:
        - Standardizes WWN format
        - Validates WWN format
        - Returns None if invalid

        Args:
            port: Remote port ID (e.g. 'rport-0:0-1')

        Returns:
            WWN of remote port or None if not found

        Example:
            ```python
            device.get_remote_port_wwn('rport-0:0-1')
            '20:00:5c:b9:01:c1:ec:71'
            ```
        """
        result = run(f'cat {self.REMOTE_PORT_PATH}/{port}/port_name')
        if result.failed:
            return None
        return self.standardize_wwn(result.stdout.strip())

    def get_remote_port_param(self, port: str, param: str) -> str | None:
        """Get remote port parameter.

        Common parameters:
        - dev_loss_tmo: Device loss timeout
        - fast_io_fail_tmo: Fast I/O failure timeout
        - node_name: FC node name
        - port_state: Port state (Online/Offline)

        Args:
            port: Remote port ID (e.g. 'rport-0:0-1')
            param: Parameter name

        Returns:
            Parameter value or None if not found

        Example:
            ```python
            device.get_remote_port_param('rport-0:0-1', 'dev_loss_tmo')
            '60'
            ```
        """
        result = run(f'cat {self.REMOTE_PORT_PATH}/{port}/{param}')
        if result.failed:
            return None
        return result.stdout.strip()

    def set_remote_port_param(self, port: str, param: str, value: str) -> bool:
        """Set remote port parameter.

        Configures port behavior:
        - Timeout values
        - Port states
        - Operating parameters

        Args:
            port: Remote port ID (e.g. 'rport-0:0-1')
            param: Parameter name
            value: Parameter value

        Returns:
            True if successful, False otherwise

        Example:
            ```python
            device.set_remote_port_param('rport-0:0-1', 'dev_loss_tmo', '60')
            True
            ```
        """
        result = run(f'echo {value} > {self.REMOTE_PORT_PATH}/{port}/{param}')
        return result.succeeded

    @classmethod
    def get_hosts(cls) -> list[str]:
        """Get list of FC hosts.

        Lists all FC HBAs in the system:
        - Traditional FC HBAs
        - FCoE CNAs
        - Virtual HBAs

        Returns:
            List of host IDs

        Example:
            ```python
            FcDevice.get_hosts()
            ['0', '1']
            ```
        """
        result = run(f'ls {cls.HOST_PATH}')
        if result.failed:
            return []
        return [h.removeprefix('host') for h in result.stdout.splitlines()]

    @classmethod
    def get_host_wwn(cls, host_id: str) -> str | None:
        """Get WWN of FC host.

        Reads port_name from sysfs:
        - Standardizes WWN format
        - Validates WWN format
        - Returns None if invalid

        Args:
            host_id: Host ID

        Returns:
            WWN of host or None if not found

        Example:
            ```python
            FcDevice.get_host_wwn('0')
            '10:00:5c:b9:01:c1:ec:71'
            ```
        """
        result = run(f'cat {cls.HOST_PATH}/host{host_id}/port_name')
        if result.failed:
            return None
        return cls.standardize_wwn(result.stdout.strip())

    @classmethod
    def get_host_transport_type(cls, host_id: str) -> TransportType | None:
        """Get transport type of FC host.

        Determines type through multiple methods:
        1. Model name matching
        2. Symbolic name inspection
        3. Driver name checking

        Args:
            host_id: Host ID

        Returns:
            Transport type or None if not found

        Example:
            ```python
            FcDevice.get_host_transport_type('0')
            'FC'
            ```
        """
        # Common model to transport type mapping
        model_map: dict[str, TransportType] = {
            'QLE2462': 'FC',  # QLogic 4Gb FC
            'QLE2772': 'FC',  # QLogic 32Gb FC
            'QLE8262': 'FCoE',  # QLogic 10Gb FCoE
            'QLE8362': 'FCoE',  # QLogic 10Gb FCoE
            'CN1000Q': 'FCoE',  # Cavium/QLogic FCoE
            'QLogic-1020': 'FCoE',  # QLogic FCoE
            '554FLR-SFP+': 'FCoE',  # HP FCoE
            'Intel 82599': 'FCoE',  # Intel FCoE
        }

        # Try to get model from sysfs
        result = run(f'cat {cls.HOST_PATH}/host{host_id}/model_name')
        if result.succeeded:
            model = result.stdout.strip()
            if model in model_map:
                return model_map[model]

        # Try to get from symbolic name
        result = run(f'cat {cls.HOST_PATH}/host{host_id}/symbolic_name')
        if result.succeeded:
            name = result.stdout.lower()
            if 'fibre channel' in name:
                return 'FC'
            if 'fcoe' in name:
                return 'FCoE'

        # Try to get from driver
        result = run(f'cat {cls.HOST_PATH}/host{host_id}/driver_name')
        if result.succeeded:
            driver = result.stdout.strip()
            if driver in {'bnx2fc', 'qedf'}:  # FCoE drivers
                return 'FCoE'

        return None

    @classmethod
    def get_all(cls) -> list[FcDevice]:
        """Get list of all FC devices.

        Discovery process:
        1. Find all FC hosts
        2. Get targets for each host
        3. Extract device names
        4. Create device objects

        Returns:
            List of FcDevice instances

        Example:
            ```python
            FcDevice.get_all()
            [FcDevice(name='sda', ...), FcDevice(name='sdb', ...)]
            ```
        """
        # Get all FC hosts
        hosts = cls.get_hosts()

        # Get all targets for each host
        all_targets: list[tuple[str, str]] = []
        for host_id in hosts:
            result = run(f'ls -1 /sys/class/fc_host/host{host_id}/device/target*')
            if result.succeeded:
                all_targets.extend((target, host_id) for target in result.stdout.splitlines())

        # Extract device names from target paths
        device_info: list[tuple[str, str]] = []
        for target, host_id in all_targets:
            name = Path(target).name
            if name:
                device_info.append((name, host_id))
            else:
                logging.warning(f'Invalid target path: {target}')

        # Create device objects with host information
        return [cls(name=name, host_id=host_id) for name, host_id in device_info]

remote_ports: list[str] property

Get remote ports.

Lists FC ports connected to this host: - Format: rport-H:B-R - H: Host number - B: Bus number - R: Remote port number

Returns:

Type Description
list[str]

List of remote port IDs

Example
device.remote_ports
['rport-0:0-1', 'rport-0:0-2']

__post_init__()

Initialize FC device.

Discovery process: 1. Set device path if needed 2. Find host ID from SCSI device link 3. Get WWN from host information 4. Determine transport type 5. Validate WWN format

Raises:

Type Description
DeviceError

If device cannot be initialized

Source code in sts_libs/src/sts/fc.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def __post_init__(self) -> None:
    """Initialize FC device.

    Discovery process:
    1. Set device path if needed
    2. Find host ID from SCSI device link
    3. Get WWN from host information
    4. Determine transport type
    5. Validate WWN format

    Raises:
        DeviceError: If device cannot be initialized
    """
    # Set path based on name if not provided
    if not self.path and self.name:
        self.path = f'/dev/{self.name}'

    # Initialize parent class
    super().__post_init__()

    # Discover host_id from SCSI device link
    if not self.host_id and self.name:
        scsi_device_path = Path(f'/sys/block/{self.name}/device')
        if scsi_device_path.exists():
            # Extract host ID from symlink like:
            # /sys/devices/pci0000:00/0000:00:03.0/0000:08:00.0/host7/rport-7:0-0/target7:0:0/7:0:0:1
            match = re.search(r'host(\d+)', str(scsi_device_path.resolve()))
            if match:
                self.host_id = match.group(1).strip()

    # Discover WWN from host information
    if not self.wwn and self.host_id:
        self.wwn = self.get_host_wwn(self.host_id)

    # Discover transport type from host
    if not self.transport_type and self.host_id:
        self.transport_type = self.get_host_transport_type(self.host_id)

    # Validate WWN format if provided
    if self.wwn and not self.is_valid_wwn(self.wwn):
        raise DeviceError(f'Invalid WWN format: {self.wwn}')

get_all() classmethod

Get list of all FC devices.

Discovery process: 1. Find all FC hosts 2. Get targets for each host 3. Extract device names 4. Create device objects

Returns:

Type Description
list[FcDevice]

List of FcDevice instances

Example
FcDevice.get_all()
[FcDevice(name='sda', ...), FcDevice(name='sdb', ...)]
Source code in sts_libs/src/sts/fc.py
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
@classmethod
def get_all(cls) -> list[FcDevice]:
    """Get list of all FC devices.

    Discovery process:
    1. Find all FC hosts
    2. Get targets for each host
    3. Extract device names
    4. Create device objects

    Returns:
        List of FcDevice instances

    Example:
        ```python
        FcDevice.get_all()
        [FcDevice(name='sda', ...), FcDevice(name='sdb', ...)]
        ```
    """
    # Get all FC hosts
    hosts = cls.get_hosts()

    # Get all targets for each host
    all_targets: list[tuple[str, str]] = []
    for host_id in hosts:
        result = run(f'ls -1 /sys/class/fc_host/host{host_id}/device/target*')
        if result.succeeded:
            all_targets.extend((target, host_id) for target in result.stdout.splitlines())

    # Extract device names from target paths
    device_info: list[tuple[str, str]] = []
    for target, host_id in all_targets:
        name = Path(target).name
        if name:
            device_info.append((name, host_id))
        else:
            logging.warning(f'Invalid target path: {target}')

    # Create device objects with host information
    return [cls(name=name, host_id=host_id) for name, host_id in device_info]

get_host_transport_type(host_id) classmethod

Get transport type of FC host.

Determines type through multiple methods: 1. Model name matching 2. Symbolic name inspection 3. Driver name checking

Parameters:

Name Type Description Default
host_id str

Host ID

required

Returns:

Type Description
TransportType | None

Transport type or None if not found

Example
FcDevice.get_host_transport_type('0')
'FC'
Source code in sts_libs/src/sts/fc.py
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
@classmethod
def get_host_transport_type(cls, host_id: str) -> TransportType | None:
    """Get transport type of FC host.

    Determines type through multiple methods:
    1. Model name matching
    2. Symbolic name inspection
    3. Driver name checking

    Args:
        host_id: Host ID

    Returns:
        Transport type or None if not found

    Example:
        ```python
        FcDevice.get_host_transport_type('0')
        'FC'
        ```
    """
    # Common model to transport type mapping
    model_map: dict[str, TransportType] = {
        'QLE2462': 'FC',  # QLogic 4Gb FC
        'QLE2772': 'FC',  # QLogic 32Gb FC
        'QLE8262': 'FCoE',  # QLogic 10Gb FCoE
        'QLE8362': 'FCoE',  # QLogic 10Gb FCoE
        'CN1000Q': 'FCoE',  # Cavium/QLogic FCoE
        'QLogic-1020': 'FCoE',  # QLogic FCoE
        '554FLR-SFP+': 'FCoE',  # HP FCoE
        'Intel 82599': 'FCoE',  # Intel FCoE
    }

    # Try to get model from sysfs
    result = run(f'cat {cls.HOST_PATH}/host{host_id}/model_name')
    if result.succeeded:
        model = result.stdout.strip()
        if model in model_map:
            return model_map[model]

    # Try to get from symbolic name
    result = run(f'cat {cls.HOST_PATH}/host{host_id}/symbolic_name')
    if result.succeeded:
        name = result.stdout.lower()
        if 'fibre channel' in name:
            return 'FC'
        if 'fcoe' in name:
            return 'FCoE'

    # Try to get from driver
    result = run(f'cat {cls.HOST_PATH}/host{host_id}/driver_name')
    if result.succeeded:
        driver = result.stdout.strip()
        if driver in {'bnx2fc', 'qedf'}:  # FCoE drivers
            return 'FCoE'

    return None

get_host_wwn(host_id) classmethod

Get WWN of FC host.

Reads port_name from sysfs: - Standardizes WWN format - Validates WWN format - Returns None if invalid

Parameters:

Name Type Description Default
host_id str

Host ID

required

Returns:

Type Description
str | None

WWN of host or None if not found

Example
FcDevice.get_host_wwn('0')
'10:00:5c:b9:01:c1:ec:71'
Source code in sts_libs/src/sts/fc.py
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
@classmethod
def get_host_wwn(cls, host_id: str) -> str | None:
    """Get WWN of FC host.

    Reads port_name from sysfs:
    - Standardizes WWN format
    - Validates WWN format
    - Returns None if invalid

    Args:
        host_id: Host ID

    Returns:
        WWN of host or None if not found

    Example:
        ```python
        FcDevice.get_host_wwn('0')
        '10:00:5c:b9:01:c1:ec:71'
        ```
    """
    result = run(f'cat {cls.HOST_PATH}/host{host_id}/port_name')
    if result.failed:
        return None
    return cls.standardize_wwn(result.stdout.strip())

get_hosts() classmethod

Get list of FC hosts.

Lists all FC HBAs in the system: - Traditional FC HBAs - FCoE CNAs - Virtual HBAs

Returns:

Type Description
list[str]

List of host IDs

Example
FcDevice.get_hosts()
['0', '1']
Source code in sts_libs/src/sts/fc.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
@classmethod
def get_hosts(cls) -> list[str]:
    """Get list of FC hosts.

    Lists all FC HBAs in the system:
    - Traditional FC HBAs
    - FCoE CNAs
    - Virtual HBAs

    Returns:
        List of host IDs

    Example:
        ```python
        FcDevice.get_hosts()
        ['0', '1']
        ```
    """
    result = run(f'ls {cls.HOST_PATH}')
    if result.failed:
        return []
    return [h.removeprefix('host') for h in result.stdout.splitlines()]

get_remote_port_param(port, param)

Get remote port parameter.

Common parameters: - dev_loss_tmo: Device loss timeout - fast_io_fail_tmo: Fast I/O failure timeout - node_name: FC node name - port_state: Port state (Online/Offline)

Parameters:

Name Type Description Default
port str

Remote port ID (e.g. 'rport-0:0-1')

required
param str

Parameter name

required

Returns:

Type Description
str | None

Parameter value or None if not found

Example
device.get_remote_port_param('rport-0:0-1', 'dev_loss_tmo')
'60'
Source code in sts_libs/src/sts/fc.py
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
def get_remote_port_param(self, port: str, param: str) -> str | None:
    """Get remote port parameter.

    Common parameters:
    - dev_loss_tmo: Device loss timeout
    - fast_io_fail_tmo: Fast I/O failure timeout
    - node_name: FC node name
    - port_state: Port state (Online/Offline)

    Args:
        port: Remote port ID (e.g. 'rport-0:0-1')
        param: Parameter name

    Returns:
        Parameter value or None if not found

    Example:
        ```python
        device.get_remote_port_param('rport-0:0-1', 'dev_loss_tmo')
        '60'
        ```
    """
    result = run(f'cat {self.REMOTE_PORT_PATH}/{port}/{param}')
    if result.failed:
        return None
    return result.stdout.strip()

get_remote_port_wwn(port)

Get WWN of remote port.

Reads port_name from sysfs: - Standardizes WWN format - Validates WWN format - Returns None if invalid

Parameters:

Name Type Description Default
port str

Remote port ID (e.g. 'rport-0:0-1')

required

Returns:

Type Description
str | None

WWN of remote port or None if not found

Example
device.get_remote_port_wwn('rport-0:0-1')
'20:00:5c:b9:01:c1:ec:71'
Source code in sts_libs/src/sts/fc.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def get_remote_port_wwn(self, port: str) -> str | None:
    """Get WWN of remote port.

    Reads port_name from sysfs:
    - Standardizes WWN format
    - Validates WWN format
    - Returns None if invalid

    Args:
        port: Remote port ID (e.g. 'rport-0:0-1')

    Returns:
        WWN of remote port or None if not found

    Example:
        ```python
        device.get_remote_port_wwn('rport-0:0-1')
        '20:00:5c:b9:01:c1:ec:71'
        ```
    """
    result = run(f'cat {self.REMOTE_PORT_PATH}/{port}/port_name')
    if result.failed:
        return None
    return self.standardize_wwn(result.stdout.strip())

is_valid_wwn(wwn) staticmethod

Check if WWN is valid.

WWN format: 8 pairs of hex digits separated by colons Example: ```python 10:00:5c:b9:01:c1:ec:71

Args: wwn: World Wide Name to check

Returns: True if valid, False otherwise

Example: FcDevice.is_valid_wwn('10:00:5c:b9:01:c1:ec:71') True ```

Source code in sts_libs/src/sts/fc.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
@staticmethod
def is_valid_wwn(wwn: str) -> bool:
    """Check if WWN is valid.

    WWN format: 8 pairs of hex digits separated by colons
    Example:
        ```python
        10:00:5c:b9:01:c1:ec:71

    Args:
            wwn: World Wide Name to check

    Returns:
            True if valid, False otherwise

    Example:
            FcDevice.is_valid_wwn('10:00:5c:b9:01:c1:ec:71')
            True
        ```
    """
    return bool(WWN_PATTERN.match(wwn.lower()))

set_remote_port_param(port, param, value)

Set remote port parameter.

Configures port behavior: - Timeout values - Port states - Operating parameters

Parameters:

Name Type Description Default
port str

Remote port ID (e.g. 'rport-0:0-1')

required
param str

Parameter name

required
value str

Parameter value

required

Returns:

Type Description
bool

True if successful, False otherwise

Example
device.set_remote_port_param('rport-0:0-1', 'dev_loss_tmo', '60')
True
Source code in sts_libs/src/sts/fc.py
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
def set_remote_port_param(self, port: str, param: str, value: str) -> bool:
    """Set remote port parameter.

    Configures port behavior:
    - Timeout values
    - Port states
    - Operating parameters

    Args:
        port: Remote port ID (e.g. 'rport-0:0-1')
        param: Parameter name
        value: Parameter value

    Returns:
        True if successful, False otherwise

    Example:
        ```python
        device.set_remote_port_param('rport-0:0-1', 'dev_loss_tmo', '60')
        True
        ```
    """
    result = run(f'echo {value} > {self.REMOTE_PORT_PATH}/{port}/{param}')
    return result.succeeded

standardize_wwn(wwn) staticmethod

Standardize WWN format.

Converts various WWN formats to standard format: - Removes '0x' prefix - Converts to lowercase - Adds colons between pairs - Validates final format

Parameters:

Name Type Description Default
wwn str

World Wide Name to standardize

required

Returns:

Type Description
str | None

Standardized WWN or None if invalid

Example
FcDevice.standardize_wwn('500A0981894B8DC5')
'50:0a:09:81:89:4b:8d:c5'
Source code in sts_libs/src/sts/fc.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
@staticmethod
def standardize_wwn(wwn: str) -> str | None:
    """Standardize WWN format.

    Converts various WWN formats to standard format:
    - Removes '0x' prefix
    - Converts to lowercase
    - Adds colons between pairs
    - Validates final format

    Args:
        wwn: World Wide Name to standardize

    Returns:
        Standardized WWN or None if invalid

    Example:
        ```python
        FcDevice.standardize_wwn('500A0981894B8DC5')
        '50:0a:09:81:89:4b:8d:c5'
        ```
    """
    if not wwn:
        return None

    # Remove 0x and : characters
    wwn = re.sub('0x', '', wwn.lower()).replace(':', '')

    # Add : every 2 characters
    wwn = ':'.join(wwn[i : i + 2] for i in range(0, len(wwn), 2))

    return wwn if FcDevice.is_valid_wwn(wwn) else None