Executing a batch of commands on multiple IBM SVC/Storwize storage systems

Sometimes you have to change several configuration parameters or run multiple commands over a list of IBM SVC storage systems. Here is my small Python 3 script to do the stuff quickly. It requires Paramiko module to handle SSH interactions.

Edit svcrun_conf.py to set targets, logins, passwords and all other parameters.

Usage: ./svcrun.py

#!/usr/bin/python3

# svcrun.py - using SSH, execute a batch of commands on multiple IBM SVC/Storwize storage systems
# 
# Python 3 and paramiko module are required
# Edit svcrun_conf.py to set targets, logins, passwords and all other parameters
#
# Usage:
#  ./svcrun.py
#
# 2017.09.14  v 1.0   Mikhail Zakharov <zmey20000@yahoo.com>

import sys
import datetime
import paramiko

# Configurable values are in svcrun_conf.py
import svcrun_conf


def ssh_exec(command, target, port=22):
    """Execute a command via SSH and read results"""

    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    try:
        client.connect(target[0], username=target[1], password=target[2], port=port)
    except:
        return 'Error: {user}@{target}: is inaccessible: {error}'.format(user=target[1], target=target[0],
                                                                         error=sys.exc_info()[1])

    stdin, stdout, stderr = client.exec_command(command)

    error = stderr.read()
    if error:
        error = error.decode('US-ASCII')
        client.close()
        return 'Error: {user}@{target}: "{command}" has failed: {error}'.format(target=target[0], user=target[1],
                                                                                command=command, error=error)

    data = stdout.read()
    client.close()

    try:
        data = data.decode('UTF-8')
    except UnicodeDecodeError:
        data = data.decode('US-ASCII')

    return data


def runcommand(target, command):
    dt = '{0:%Y-%m-%d-%H-%M-%S}'.format(datetime.datetime.now())

    print(dt, 'Run command: "{cmd}"'.format(cmd=command), file=sys.stderr)
    print(ssh_exec(command, target), file=open(dt + '_' + target[0] + '_' + command.split()[0] + '.out', 'a'))


for target in svcrun_conf.svc_targets:
    for command in svcrun_conf.svc_commands:
        runcommand(target, command)

print('Done', file=sys.stderr)

Configuration file is required:

# Configuration file for svcsrun.py

# Default SVC/Storwize credentials
svc_username = 'username'
svc_password = 'password'

# SVC/Storwize systems to run commands on
svc_targets = {
    # 'Hostname/IP', 'Login', 'Password'),
    ('192.168.1.11', svc_username, svc_password),
    ('192.168.1.12', 'anotheruser', 'secret')
}

# Commands to run one by one on each SVC/Storwize system
svc_commands = [
    'lssystem -delim \,',
    'lsvdisk -nohdr -bytes -delim \,'
]

Download it from GitHub

Advertisements
Posted in Storage Automation, Tips & tricks | Tagged , , , , , , , , , | Leave a comment

Scan IBM SVC/Storwize system for “SCSI ID Mismatches”

A small addition to the SAN/Storage CLI Toolbox which shows so called SCSI ID (LUN) mismatches on IBM SVC/Storwize systems. This Bourne shell/awk script shows the list of the same vdisks that are mapped with different SCSI IDs. Should be useful for clusters, especially VMware.

#!/bin/sh

# -----------------------------------------------------------------------------
# "THE BEER-WARE LICENSE" (Revision 42):
# zmey20000@yahoo.com wrote this file. As long as you retain this notice you
# can do whatever you want with this stuff. If we meet some day, and you think
# this stuff is worth it, you can buy me a beer in return Mikhail Zakharov
# -----------------------------------------------------------------------------

# simmatch.sh - Scan IBM SVC/Storwize system for "SCSI ID Mismatches"
# 2017.08.31    v1.0

delim=","			# Main delimiter
delim2=";"			# Second layer delimiter
fashion="../fashion/fashion"	# fashion SSH wrapper. Comment/uncomment the
				# appropriate lines below if you want pure SSH

lshostvdiskmap="lshostvdiskmap -nohdr -delim $delim"

# ----------------------------------------------------------------------------- 
usage() {
    echo "Usage:"
    echo " simmatch.sh <target> <user> <password>"
    exit 1
}
  
# ----------------------------------------------------------------------------- 
[ "$#" -ne 3 ] && usage

target="$1"
user="$2"
password="$3"

awk="gawk"				# We need gawk to do the stuff

# Uncomment ssh and comment fashion if you like
#ssh "$user"@"$target" "$lshostvdiskmap" | $awk -F "$delim" -v d2=$delim2 '
$fashion "$target" "$user" "$password" "$lshostvdiskmap" | 
	$awk -F "$delim" -v d2=$delim2 '
{ 
	# Put all records into an array
	for (f = 1; f <= NF; f++)
		dm[NR, f] = $f
	dm[NR, NF + 1] = 0		# Mapping count
	dm[NR, NF + 2] = ""		# SCSI ID List
}
END {
	OFS = FS
	for (l = 1; l <= NR; l++)
		for (l1 = 1; l1 <= NR; l1++)
			if (dm[l, 4] == dm[l1, 4]) {
				dm[l, NF + 1] += 1
				dm[l, NF + 2] = dm[l, NF + 2] dm[l1, 3] OFS
			}
	# Pack SCSI ID List
	for (l = 1; l <= NR; l++) if (dm[l, 9] > 1) {
			len_sidlst = split(dm[l, NF + 2], sidlst)
			# get unique SCSI IDs only
			delete usidlst
			for (le = 1; le <= len_sidlst; le++)
				usidlst[sidlst[le]]++
			# Save unique SCSI IDs back to the list
			delete dm[l, NF + 2]
			for (sid in usidlst)
				if (sid != "")
					dm[l, NF + 2] = dm[l, NF + 2] sid d2
		}
	# Print out report
	print "Host ID", "Host Name", "SCSI ID", "Vdisk ID",\
		"Vdisk Name", "Vdisk UID", "IO Group ID",\
		"IO Group Name", "Mapping count", "SCSI ID List"
	for (l = 1; l <= NR; l++) if (split(dm[l, NF + 2], sidlst, d2) > 2) {
			for (f = 1; f < NF + 2; f++)
				printf dm[l, f] OFS
			# Ugly strip of the last "d2" occurrence
			printf substr(dm[l, NF + 2], 1, length(dm[l, NF + 2]) - 1)"\n"
		}
} 
'

Download it from GitHub.

Posted in Storage Automation, Tips & tricks | Tagged , , , , , , , , , , , , | Leave a comment

Accessing HP/3PAR storage systems using Python and SSH

I have just found a small script on Python 3 which I wrote for D81S to test a connection with HP/3PAR 7xxx arrays. It still looks like a good example of the subject, so I decided to store it for the future reference.

Posted in Storage Automation, Tips & tricks | Tagged , , , , , | Leave a comment

Short memo on COUNTIFS(), SUMIFS() and VLOOKUP() functions in MS Excel

Not using Excel very often, I sometimes have to waste my time searching Office support site for these functions in order to refresh in memory their tricky syntax and usage limitations.

Today I decided to gather everything I know about these functions in a single source to keep it at hand when using Excel or LibreOffice Calc (with minor syntax changes).

Descriptions of the lightweight versions of these functions are available on the Office support site: COUNTIF(), SUMIF(), LOOKUP(). Continue reading

Posted in Tips & tricks | Tagged , , , , , | Leave a comment

LLFD – Link, Login, Fabric, Devices

Have just prepared a brief cheat sheet to remind which processes begin when we connect an HBA with the Brocade Fiber Channel switch port, in other words, when a device joins a fabric.

LINK

  • Signal transition (light/electrical)
  • Speed negotiation
  • Character and Word synchronization
  • Link initialization primitives (transition to active state, ready to send frames)

LOGIN

  • FLOGI – Request Fabric Login port (FFFFFE) for Port ID (PID) by sending NWWN/PWWN and exchange buffer credits capabilities with the switch port. Switch populates NS with PID, NWWN/PWWN
  • PLOGI – Port Login to Name Server (FFFFFC) to be able to register information in NS
  • SCR – State Change Registration at Fabric Controller (FFFFFD) to receive Registered State Change Notifications (RSCN) on events of online/offline of the zone members. SCR is mandatory for initiators
  • NS registration – Register additional attributes in NS: Node / port names, vendor, device model, firmware and driver versions. Separate requests are done for each attribute

FABRIC

  • Initiator requests FC-addresses of zoned devices. Makes additional requests to NS to get WWNs, etc.
  • Device probe – switch probes devices for additional information to populate NS with information from targets. Typically initiators (hosts) reject probes, while targets (storage systems) accept.

DEVICES

  • Initiator sends PLOGI to zoned targets
  • Initiator sends Process Login (PRLI) to targets
  • Start SCSI transmissions

Posted in Storage | Tagged , , , , , , , , , | Leave a comment

What is the name of a new drive in my FreeBSD box

or a quick way to list GEOM configuration in FreeBSD

Drives related information questions, including that one in the title, may be tricky for a new (and sometimes for an experienced) FreeBSD user, and even Handbook advises to see the “dmesg” output to check the drives when adding new drives:

“Inspect `/var/run/dmesg.boot` to ensure the new disk was found. In this example, the newly added SATA drive will appear as `ada1`.”

Actually, there are many methods and utilities to check drives, and one of the most powerful tool is “camcontrol”. But what if you are dealing with GEOM stack or checking a partition? The solution is to check GEOM configuration:

sysctl -n kern.geom.conftxt

or

sysctl -n kern.geom.confxml

Yes, this is not a panacea, but it can give a hint.

I have written a simple script (geom_show) which can be used to process GEOM configuration and print out basic information about volumes: mediasize, sectorsize, stripesize and etc. It doesn’t not require root privileges and runs without any additional dependencies. And, hah, it makes me feel like an old programmer because it actually consists of pure AWK code and a wrapper in Bourne shell 😉

I hope, it can help someone to make things simpler, and, please, let me know if it doesn’t.

#!/bin/sh

# Copyright (c) 2017 Mikhail Zakharov <zmey20000@yahoo.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

#
# Show GEOM configuration
#
# Typical usage:
# 	geom_show.sh -l -c DISK
#
# 2017.07.25	 1.0

# Defaults ---------------------------------------------------------------------
ofs=","					# Output Field Separator is "comma"
show_headers=true			# Show headers in output

# ------------------------------------------------------------------------------
usage()
{
	error_code=$1			# Error code on exit() to the caller
	error_text="$2"			# Short error message to print
	usage_help="Usage:
  geom_show.sh -k [-s separator]
	List disk names known by the kernel.
  geom_show.sh -g [-s separator]
  geom_show.sh -g -c CLASS [-s separator]
	List GEOM provider names of all or any particular class.
  geom_show.sh -l -c CLASS | -p PROVIDER [-h] [-s separator]
	Show details of GEOM provider(s). Filter entries by CLASS or exact
	PROVIDER name. Suppress headers with -h key.
  geom_show.sh -d [-s separator]
	Dump raw GEOM configuration.
Use [-s separator] to specify output field separator. Default is comma: (,)."

	[ "$error_text" ] && printf "Error: $error_text\n"
	printf "$usage_help\n"
	exit $error_code
}

# -----------------------------------------------------------------------------
awk="/usr/bin/awk"
sed="/usr/bin/sed"
sysctl="/sbin/sysctl"

# Error messages --------------------------------------------------------------
err_kgld_flags="Options -k, -g, -l or -d cannot be combined together."
err_no_flags="Specify -k, -g, -l or -d option to show disks configuration."
err_cp_flags="Specify -c, -p flag or both."

geom_show='#!/usr/bin/awk -f
# Copyright (c) 2017 Mikhail Zakharov <zmey20000@yahoo.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# Parse GEOM configuration
#
# Typical usage:
# sysctl -n kern.geom.confxml | /path/to/geom_show.awk -v "class=DISK"
#
# Other options:
#	-v provider="ada0"	Specify GEOM provider to show
#	-v noheader="yes"	Omit header
#	-v short="yes"		Short form prints only providers name(s)
# 2017.07.25	v 1.0
BEGIN {
	FS = ">|<"
	if (!ofs)
		ofs = ","
	OFS = ofs
	gprov = provider
	gclass = class
	if (!gprov && !gclass)
		short = "yes"
	if (gclass && !gprov && !short && noheader != "yes")
		print_header(gclass)
}
function expanded_length(s) { gsub("\t", "        ", s); return length(s) }
function print_header(gclass) {
	common_header = "Class" ofs "Provider" ofs "Mediasize" ofs \
		"Sectorsize" ofs "Stripesize" ofs "Stripeoffset"
	disk_header = common_header ofs "Heads" ofs "Sectors" ofs "RPM" ofs \
		"Ident" ofs "Description"
	md_header =  common_header ofs "Heads" ofs "Sectors" ofs "Compression" \
		ofs "Access" ofs "Type"
	part_header = common_header ofs "Start" ofs "End" ofs "Index" ofs "Type"
	if (gclass == "DISK")
		print disk_header
	else if (gclass == "MD")
		print md_header
	else if (gclass == "PART")
		print part_header
	else print common_header
}
/<name>/,/<\/name>/ {
	depth = expanded_length($1)
	if (depth == 4)
		if ($3 == gclass || !gclass) {
		# We have found requested GEOM Class
			in_class = 1
			current_gclass = $3
		} else
			in_class = 0
	if (in_class && depth == 10) {
		if (gprov == $3) {
			# GEOM class for the given provider is detected
			gclass = current_gclass
			if (gclass && !short && noheader != "yes")
				print_header(gclass)
		}
		# This should be the GEOM Name
		gname = $3
	}
}
/<mediasize>/ || /<sectorsize>/ || /<stripesize>/ || /<stripeoffset>/ {
	if (!short && in_class && expanded_length($1) == 10)
	# These are common, default fields for all providers
			gdata = gdata ofs $3
	next
}
/<fwheads>/ || /<fwsectors>/ {
	if (!short && in_class && expanded_length($1) == 12)
		if (gclass == "DISK" || gclass == "MD")
			gdata = gdata ofs $3
	next
}
/<rotationrate>/ || /<ident>/ || /<descr>/ {
	if (!short && in_class && expanded_length($1) == 12 && gclass == "DISK")
		gdata = gdata ofs $3
	next
}
/<compression>/ || /<access>/ {
	if (!short && in_class && expanded_length($1) == 12 && gclass == "MD")
		gdata = gdata ofs $3
	next
}
/<type>/ {
	if (!short && in_class && expanded_length($1) == 12)
		if (gclass == "MD" || gclass == "PART")
			gdata = gdata ofs $3
	next
}
/<start>/ || /<end>/ || /<index>/ {
	if (!short && in_class && expanded_length($1) == 12 && gclass == "PART")
		gdata = gdata ofs $3
	next
}
/<\/provider>/  {
	if (in_class && expanded_length($1) == 8) {
		if (!gprov || gprov == gname)
			if (current_gclass != "LABEL" &&
				current_gclass != "DEV" ||
				current_gclass == gclass)
					if (!short)
						print gclass ofs gname gdata
					else {
						if (!entry_1)
							printf gname
						else
							printf ofs gname
						entry_1 = 1
					}
		gdata = ""
	}
}
END {
	if (short)
		printf "\n"
}'

# ------------------------------------------------------------------------------
while getopts "kgldc:p:hs:" flag
do
	case "$flag" in
		k)      k_flag=true ;;
		g)	g_flag=true ;;
		l)	l_flag=true ;;
		d)	d_flag=true ;;
		c)	c_flag=true; c_arg="$OPTARG" ;;
		p)	p_flag=true; p_arg="$OPTARG";;
		h)	h_flag=true ;;
		s)	s_flag=true; s_arg="$OPTARG" ;;
		*)      usage 1 ;;
	esac
done

[ ! $k_flag ] && [ ! $g_flag ] && [ ! $l_flag ] && [ ! $d_flag ] &&
	usage 1 "$err_no_flags"
[ $s_flag ] && ofs="$s_arg"

# geom_show.sh -k [-s separator] -----------------------------------------------
if [ $k_flag ] ; then
	[ $g_flag ] || [ $l_flag ] || [ $d_flag ] && usage 1 "$err_kgld_flags"

	$sysctl -n kern.disks | $sed "s/ /$ofs/g"
	exit 0
fi

# geom_show.sh -g [-s separator] | geom_show.sh -g -c CLASS [-s separator] -----
if [ $g_flag ] ; then
	[ $k_flag ] || [ $l_flag ] || [ $d_flag ] && usage 1 "$err_kgld_flags"
	[ $c_flag ] && gclass="$c_arg"

	"$sysctl" -n kern.geom.confxml |
		"$awk" -v ofs="$ofs" -v short="yes" -v class="$gclass" \
			"$geom_show"
	exit 0
fi

# geom_show.sh -l -c CLASS | -p PROVIDER [-h] [-s separator] -------------------
if [ $l_flag ] ; then
	[ $g_flag ] || [ $k_flag ] || [ $d_flag ] && usage 1 "$err_kgld_flags"
	[ ! $c_flag ] && [ ! $p_flag ] && usage 1 "$err_cp_flags"
	[ $c_flag ] && gclass="$c_arg"
	[ $p_flag ] && gprov="$p_arg"
	[ $h_flag ] && noheader="yes" 

	"$sysctl" -n kern.geom.confxml |
		"$awk" -v ofs="$ofs" -v class="$gclass" \
			-v provider="$gprov" -v noheader="$noheader" \
			"$geom_show"
	exit 0
fi

# geom_show.sh -d [-s separator] -----------------------------------------------
if [ $d_flag ] ; then
	[ $k_flag ] || [ $g_flag ] || [ $l_flag ] && usage 1 "$err_kgld_flags"

	geom_dump=`$sysctl -n kern.geom.conftxt | sed -e "s/ /$ofs/g"`
	printf "$geom_dump\n"
	exit 0
fi
#!/usr/bin/awk -f

# Copyright (c) 2017 Mikhail Zakharov <zmey20000@yahoo.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

#
# Parse GEOM configuration
#
# Typical usage:
# sysctl -n kern.geom.confxml | /path/to/geom_show.awk -v "class=DISK"
#
# Other options:
# 	-v provider="ada0"	Specify GEOM provider to show
#	-v noheader="yes"	Omit header
#	-v short="yes"		Short form prints only provider name(s)

# 2017.07.25	v 1.0

BEGIN {
	FS = ">|<"

	if (!ofs)
		ofs = ","
	OFS = ofs

	gprov = provider
	gclass = class

	if (!gprov && !gclass)
		short = "yes"

	if (gclass && !gprov && !short && noheader != "yes")
		print_header(gclass)
}

function expanded_length(s) { gsub("\t", "        ", s); return length(s) }

function print_header(gclass) {
	common_header = "Class" ofs "Provider" ofs "Mediasize" ofs \
		"Sectorsize" ofs "Stripesize" ofs "Stripeoffset"
	disk_header = common_header ofs "Heads" ofs "Sectors" ofs "RPM" ofs \
		"Ident" ofs "Description"
	md_header =  common_header ofs "Heads" ofs "Sectors" ofs "Compression" \
		ofs "Access" ofs "Type"
	part_header = common_header ofs "Start" ofs "End" ofs "Index" ofs "Type"

	if (gclass == "DISK")
		print disk_header
	else if (gclass == "MD")
		print md_header
	else if (gclass == "PART")
		print part_header
	else print common_header
}

/<name>/,/<\/name>/ {
	depth = expanded_length($1)

	if (depth == 4)
		if ($3 == gclass || !gclass) {
		# We have found requested GEOM Class
			in_class = 1
			current_gclass = $3
		} else
			in_class = 0

	if (in_class && depth == 10) {
		if (gprov == $3) {
			# GEOM class for the given provider is detected
			gclass = current_gclass
			if (gclass && !short && noheader != "yes")
				print_header(gclass)
		}
		# This should be the GEOM Name
		gname = $3
	}
}

/<mediasize>/ || /<sectorsize>/ || /<stripesize>/ || /<stripeoffset>/ {
	if (!short && in_class && expanded_length($1) == 10)
	# These are common, default fields for all providers
			gdata = gdata ofs $3
	next
}

/<fwheads>/ || /<fwsectors>/ {
	if (!short && in_class && expanded_length($1) == 12)
		if (gclass == "DISK" || gclass == "MD")
			gdata = gdata ofs $3
	next
}

/<rotationrate>/ || /<ident>/ || /<descr>/ {
	if (!short && in_class && expanded_length($1) == 12 && gclass == "DISK")
		gdata = gdata ofs $3
	next
}

/<compression>/ || /<access>/ {
	if (!short && in_class && expanded_length($1) == 12 && gclass == "MD")
		gdata = gdata ofs $3
	next
}

/<type>/ {
	if (!short && in_class && expanded_length($1) == 12)
		if (gclass == "MD" || gclass == "PART")
			gdata = gdata ofs $3
	next
}

/<start>/ || /<end>/ || /<index>/ {
	if (!short && in_class && expanded_length($1) == 12 && gclass == "PART")
		gdata = gdata ofs $3
	next
}

/<\/provider>/  {
	if (in_class && expanded_length($1) == 8) {
		if (!gprov || gprov == gname)
			if (current_gclass != "LABEL" &&
				current_gclass != "DEV" ||
				current_gclass == gclass)
					if (!short)
						print gclass ofs gname gdata
					else {
						if (!entry_1)
							printf gname
						else
							printf ofs gname
						entry_1 = 1
					}
		gdata = ""
	}
}

END {
	if (short)
		printf "\n"
}

See the code on the GitHub and on the project page of my blog.

UPD 2017.07.26. There is a nice update on diskinfo by Allan Jude.

Posted in My projects, Storage, Storage Automation | Tagged , , , | 3 Comments

Excel formulas syntax

Once, I thought embedding SQL requests into a code was the most boring thing I could imagine. Now I’m losing all my marbles while trying to push MS Excel formulas into my Python script.

Posted in Impressions, IRL | Tagged , , , | Leave a comment