Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add iscsi helpers to dump iscsi session information #108

Merged
merged 1 commit into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions drgn_tools/iscsi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Copyright (c) 2024, Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
"""
Helper for iscsi
"""
import argparse
from typing import Iterator

from drgn import Object
from drgn import Program
from drgn.helpers.common import escape_ascii_string
from drgn.helpers.common.type import enum_type_to_class
from drgn.helpers.linux.list import list_for_each_entry

from drgn_tools.corelens import CorelensModule
from drgn_tools.module import ensure_debuginfo
from drgn_tools.scsi import for_each_scsi_host
from drgn_tools.scsi import for_each_scsi_host_device
from drgn_tools.scsi import host_module_name
from drgn_tools.scsi import scsi_device_name
from drgn_tools.table import print_dictionary
from drgn_tools.util import enum_name_get

ISCSI_SESSION_STATES = ["LOGGED_IN", "FAILED", "FREE"]


def for_each_iscsi_host(prog: Program) -> Iterator[Object]:
"""
Iterate through all scsi host devices and returns an
iterator of hosts backed by iscsi drivers.
:returns: an iterator of ``struct Scsi_Host *``
"""

# additional iscsi drivers
ISCSI_MODS = [
"qla4xxx",
"bnx2i",
"cxgb3i",
"cxgb4i",
"qedi",
"iscsi_tcp",
"be2iscsi",
]
biger410 marked this conversation as resolved.
Show resolved Hide resolved
for shost in for_each_scsi_host(prog):
mod_name = host_module_name(shost).lower()
if mod_name in ISCSI_MODS:
yield shost


def for_each_iscsi_session(prog: Program) -> Iterator[Object]:
"""
Iterate through all iscsi_cls_session and gets the associated iscsi_session.
:returns: an iterator of ``struct iscsi_session *``

"""
for cls_session in list_for_each_entry(
"struct iscsi_cls_session", prog["sesslist"].address_of_(), "sess_list"
):
yield Object(
prog,
prog.type("struct iscsi_session", filename="libiscsi.h"),
address=cls_session.dd_data,
)

biger410 marked this conversation as resolved.
Show resolved Hide resolved

def print_iscsi_sessions(prog: Program) -> None:
"""
Dump iscsi sessions.

"""
msg = ensure_debuginfo(prog, ["libiscsi", "scsi_transport_iscsi"])
if msg:
print(msg)
return

output = {}

for session in reversed(list(for_each_iscsi_session(prog))):
print("**********")
conn = session.leadconn

persistent_address = escape_ascii_string(
conn.persistent_address.string_()
)
persistent_port = int(conn.persistent_port)
output[
"Scsi_Host"
] = f"host{session.host.host_no.value_()} ({hex(session.host.value_())})"
output["Session"] = hex(session.address_of_())
output["SID"] = str(int(session.cls_session.sid))
output["Persistent Portal"] = f"{persistent_address}:{persistent_port}"
output["Iface Name"] = escape_ascii_string(session.ifacename.string_())
output["Session State"] = ISCSI_SESSION_STATES[
session.cls_session.state
]
connstate = enum_type_to_class(
prog.type("enum iscsi_connection_state"), "connstate"
)
output["Connection State"] = str(
enum_name_get(
connstate,
conn.cls_conn.state,
"UNKNOWN",
)
)
output["Initiatorname"] = escape_ascii_string(
session.initiatorname.string_()
)
output["Targetname"] = escape_ascii_string(
session.targetname.string_()
)
biger410 marked this conversation as resolved.
Show resolved Hide resolved
biger410 marked this conversation as resolved.
Show resolved Hide resolved

print_dictionary(output)

print("Attached SCSI devices: ")
print("**********")
print(
"Host Number: {} STATE: {}".format(
session.host.host_no.value_(),
session.host.shost_state.format_(type_name=False),
)
)

for scsi_dev in for_each_scsi_host_device(prog, session.host):
name = scsi_device_name(prog, scsi_dev)
print(
"scsi{} Channel {} Id {} Lun: {}".format(
session.host.host_no.value_(),
int(scsi_dev.channel),
int(scsi_dev.id),
int(scsi_dev.lun),
)
)
sdev_state = enum_type_to_class(
prog.type("enum scsi_device_state"), "sdev_state"
)
state = enum_name_get(
sdev_state,
scsi_dev.sdev_state,
"UNKNOWN",
)
print(" Attached iscsi disk: {} State: {}".format(name, state))

print()


class Iscsi(CorelensModule):
"""
Print iscsi sessions.
"""

name = "iscsi"
biger410 marked this conversation as resolved.
Show resolved Hide resolved
skip_unless_have_kmod = ["libiscsi", "scsi_transport_iscsi"]

def run(self, prog: Program, args: argparse.Namespace) -> None:
print_iscsi_sessions(prog)
30 changes: 28 additions & 2 deletions drgn_tools/scsi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import drgn
from drgn import container_of
from drgn import FaultError
from drgn import Object
from drgn import Program
from drgn.helpers.linux.list import list_for_each_entry
Expand All @@ -20,9 +21,9 @@

def for_each_scsi_host(prog: Program) -> Iterator[Object]:
"""
Iterates through all scsi hosts and returns a
Iterate through all scsi hosts and returns an
iterator.
:returns: a iterator of ``struct Scsi_Host *``
:returns: an iterator of ``struct Scsi_Host *``
"""
class_in_private = prog.type("struct device_private").has_member(
"knode_class"
Expand Down Expand Up @@ -56,6 +57,31 @@ def host_module_name(shost: Object) -> str:
return name


def for_each_scsi_host_device(
prog: Program, shost: Object
) -> Iterator[Object]:
"""
Get a list of scsi_device associated with a Scsi_Host.
:returns: an iterator of ``struct scsi_device *``
"""
return list_for_each_entry(
"struct scsi_device", shost.__devices.address_of_(), "siblings"
)


def scsi_device_name(prog: Program, sdev: Object) -> str:
"""
Get the device name associated with scsi_device.
:return ``str``
"""
rq = sdev.request_queue
dev = container_of(rq.kobj.parent, "struct device", "kobj")
try:
return dev.kobj.name.string_().decode()
except FaultError:
return ""


def print_scsi_hosts(prog: Program) -> None:
"""
Prints scsi host information
Expand Down
7 changes: 7 additions & 0 deletions tests/test_iscsi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2024, Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
from drgn_tools import iscsi


def test_iscsi(prog):
iscsi.print_iscsi_sessions(prog)
Loading