
parse_fileio was a cut-n-paste from block, missing key attributes like size and buffered. Fixed. Also, bodge up better handling for single vs multiple tpgs. Clarify referring to tpgs versus the tpg tag (tpgt, an integer). Signed-off-by: Andy Grover <agrover@redhat.com>
328 lines
9.3 KiB
Python
Executable File
328 lines
9.3 KiB
Python
Executable File
#!/usr/bin/python3
|
|
'''
|
|
convert-to-json
|
|
|
|
This file is part of RTSLib-fb.
|
|
Copyright (c) 2013-2016 by Red Hat, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
not use this file except in compliance with the License. You may obtain
|
|
a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
License for the specific language governing permissions and limitations
|
|
under the License.
|
|
'''
|
|
|
|
#
|
|
# A script to convert .lio format save files to json format.
|
|
#
|
|
|
|
import json
|
|
import re
|
|
|
|
def human_to_bytes(hsize, kilo=1024):
|
|
'''
|
|
This function converts human-readable amounts of bytes to bytes.
|
|
It understands the following units :
|
|
- I{B} or no unit present for Bytes
|
|
- I{k}, I{K}, I{kB}, I{KB} for kB (kilobytes)
|
|
- I{m}, I{M}, I{mB}, I{MB} for MB (megabytes)
|
|
- I{g}, I{G}, I{gB}, I{GB} for GB (gigabytes)
|
|
- I{t}, I{T}, I{tB}, I{TB} for TB (terabytes)
|
|
|
|
Note: The definition of I{kilo} defaults to 1kB = 1024Bytes.
|
|
Strictly speaking, those should not be called I{kB} but I{kiB}.
|
|
You can override that with the optional kilo parameter.
|
|
|
|
@param hsize: The human-readable version of the Bytes amount to convert
|
|
@type hsize: string or int
|
|
@param kilo: Optional base for the kilo prefix
|
|
@type kilo: int
|
|
@return: An int representing the human-readable string converted to bytes
|
|
'''
|
|
size = hsize.replace('i', '')
|
|
size = size.lower()
|
|
if not re.match("^[0-9\.]+[k|m|g|t]?[b]?$", size):
|
|
raise Exception("Cannot interpret size, wrong format: %s" % hsize)
|
|
|
|
size = size.rstrip('ib')
|
|
|
|
units = ['k', 'm', 'g', 't']
|
|
try:
|
|
power = units.index(size[-1]) + 1
|
|
except ValueError:
|
|
power = 0
|
|
size = int(size)
|
|
else:
|
|
try:
|
|
size = int(size[:-1])
|
|
except ValueError:
|
|
size = int(float(size[:-1]))
|
|
|
|
return size * (int(kilo) ** power)
|
|
|
|
def parse_yesno(val):
|
|
if val == "yes":
|
|
return 1
|
|
elif val == "no":
|
|
return 0
|
|
else:
|
|
try:
|
|
return int(val)
|
|
except:
|
|
return val
|
|
|
|
def parse_attributes(txt, cur):
|
|
attribs = {}
|
|
while txt[cur] != "}":
|
|
name = txt[cur]
|
|
val = txt[cur+1]
|
|
attribs[name] = parse_yesno(val)
|
|
cur += 2
|
|
return (cur+1, attribs)
|
|
|
|
def parse_fileio(txt, cur):
|
|
so = dict(plugin="fileio")
|
|
while txt[cur] != "}":
|
|
if txt[cur] == "path":
|
|
so["dev"] = txt[cur+1]
|
|
cur += 2
|
|
continue
|
|
if txt[cur] == "size":
|
|
so["size"] = human_to_bytes(txt[cur+1])
|
|
cur += 2
|
|
continue
|
|
if txt[cur] == "buffered":
|
|
# skip, recent LIO doesn't use for fileio
|
|
cur += 2
|
|
continue
|
|
if txt[cur] == "attribute":
|
|
cur, so["attributes"] = parse_attributes(txt, cur+2)
|
|
continue
|
|
return (cur+1, so)
|
|
|
|
def parse_block(txt, cur):
|
|
so = dict(plugin="block")
|
|
while txt[cur] != "}":
|
|
if txt[cur] == "path":
|
|
so["dev"] = txt[cur+1]
|
|
cur += 2
|
|
continue
|
|
if txt[cur] == "attribute":
|
|
cur, so["attributes"] = parse_attributes(txt, cur+2)
|
|
continue
|
|
return (cur+1, so)
|
|
|
|
def parse_ramdisk(txt, cur):
|
|
so = dict(plugin="ramdisk")
|
|
while txt[cur] != "}":
|
|
if txt[cur] == "nullio":
|
|
so["nullio"] = parse_yesno(txt[cur+1])
|
|
cur += 2
|
|
continue
|
|
if txt[cur] == "size":
|
|
so["size"] = human_to_bytes(txt[cur+1])
|
|
cur += 2
|
|
continue
|
|
if txt[cur] == "attribute":
|
|
cur, so["attributes"] = parse_attributes(txt, cur+2)
|
|
continue
|
|
return (cur+1, so)
|
|
|
|
so_types = {
|
|
"fileio": parse_fileio,
|
|
"rd_mcp": parse_ramdisk,
|
|
"iblock": parse_block,
|
|
}
|
|
|
|
def parse_storage(txt, cur):
|
|
name = txt[cur+3]
|
|
ty = txt[cur+1]
|
|
cur += 5
|
|
(cur, d) = so_types[ty](txt, cur)
|
|
d["name"] = name
|
|
return (cur, d)
|
|
|
|
def parse_lun(txt, cur):
|
|
index = int(txt[cur+1])
|
|
plugin, name = txt[cur+3].split(":")
|
|
return cur+4, dict(index=index, plugin=plugin, name=name)
|
|
|
|
def parse_mapped_lun(txt, cur):
|
|
mlun = dict(index=txt[cur+1])
|
|
cur += 3
|
|
while txt[cur] != "}":
|
|
if txt[cur] == "target_lun":
|
|
mlun["tpg_lun"] = parse_yesno(txt[cur+1])
|
|
cur += 2
|
|
continue
|
|
if txt[cur] == "write_protect":
|
|
mlun["write_protect"] = bool(parse_yesno(txt[cur+1]))
|
|
cur += 2
|
|
continue
|
|
return cur+1, mlun
|
|
|
|
def parse_acl(txt, cur):
|
|
acl = dict(node_wwn=txt[cur+1])
|
|
mapped_luns = []
|
|
cur += 3
|
|
while txt[cur] != "}":
|
|
if txt[cur] == "attribute":
|
|
cur, acl["attributes"] = parse_attributes(txt, cur+2)
|
|
continue
|
|
if txt[cur] == "auth":
|
|
cur, auth = parse_attributes(txt, cur+2)
|
|
if len(auth):
|
|
acl["auth"] = auth
|
|
continue
|
|
if txt[cur] == "mapped_lun":
|
|
cur, mlun = parse_mapped_lun(txt, cur)
|
|
mapped_luns.append(mlun)
|
|
acl["mapped_luns"] = mapped_luns
|
|
return cur+1, acl
|
|
|
|
def parse_tpg(tag, txt, cur):
|
|
if tag is None:
|
|
tag = int(txt[cur+1])
|
|
cur += 2
|
|
tpg = dict(tag=tag)
|
|
luns = []
|
|
acls = []
|
|
portals = []
|
|
cur += 3
|
|
while txt[cur] != "}":
|
|
if txt[cur] == "enable":
|
|
tpg["enable"] = parse_yesno(txt[cur+1])
|
|
cur += 2
|
|
continue
|
|
if txt[cur] == "attribute":
|
|
cur, tpg["attributes"] = parse_attributes(txt, cur+2)
|
|
continue
|
|
if txt[cur] == "parameter":
|
|
cur, tpg["parameters"] = parse_attributes(txt, cur+2)
|
|
continue
|
|
if txt[cur] == "auth":
|
|
cur, auth = parse_attributes(txt, cur+2)
|
|
if len(auth):
|
|
tpg["auth"] = auth
|
|
continue
|
|
if txt[cur] == "lun":
|
|
cur, l = parse_lun(txt, cur)
|
|
luns.append(l)
|
|
continue
|
|
if txt[cur] == "acl":
|
|
cur, acl = parse_acl(txt, cur)
|
|
acls.append(acl)
|
|
continue
|
|
if txt[cur] == "portal":
|
|
ip, port = txt[cur+1].split(":")
|
|
portal = dict(ip_address=ip, port=port)
|
|
portals.append(portal)
|
|
cur += 2
|
|
continue
|
|
if len(luns):
|
|
tpg["luns"] = luns
|
|
if len(acls):
|
|
tpg["node_acls"] = acls
|
|
if len(portals):
|
|
tpg["portals"] = portals
|
|
return cur+1, tpg
|
|
|
|
|
|
def parse_target(fabric, txt, cur):
|
|
target = dict(wwn=txt[cur+1], fabric=fabric)
|
|
tpgs = []
|
|
tpgt = None
|
|
# handle multiple tpgts
|
|
if txt[cur+2] == "{":
|
|
extra = 1
|
|
else:
|
|
extra = 0
|
|
tpgt = int(txt[cur+3])
|
|
cur += 2 + extra
|
|
while txt[cur] != "}":
|
|
cur, tpg = parse_tpg(tpgt, txt, cur)
|
|
tpgs.append(tpg)
|
|
target["tpgs"] = tpgs
|
|
return cur+extra, target
|
|
|
|
def parse_fabric(txt, cur):
|
|
fabric = txt[cur+1]
|
|
cur += 3
|
|
while txt[cur] != "}":
|
|
if txt[cur] == "discovery_auth":
|
|
cur, disco = parse_attributes(txt, cur+2)
|
|
new_disco = {}
|
|
if disco.get("enable"):
|
|
new_disco["discovery_enable_auth"] = disco.get("enable")
|
|
if disco.get("userid"):
|
|
new_disco["discovery_userid"] = disco.get("userid")
|
|
if disco.get("password"):
|
|
new_disco["discovery_password"] = disco.get("password")
|
|
if disco.get("mutual_userid"):
|
|
new_disco["discovery_mutual_userid"] = disco.get("mutual_userid")
|
|
if disco.get("mutual_password"):
|
|
new_disco["discovery_mutual_password"] = disco.get("mutual_password")
|
|
new_disco["name"] = "iscsi"
|
|
fabs.append(new_disco)
|
|
continue
|
|
if txt[cur] == "target":
|
|
cur, t = parse_target(fabric, txt, cur)
|
|
targs.append(t)
|
|
continue
|
|
return cur
|
|
|
|
sos = []
|
|
fabs = []
|
|
targs = []
|
|
|
|
# a basic tokenizer that splits on whitespace and handles double quotes
|
|
def split(s):
|
|
new_lst = []
|
|
in_quotes = False
|
|
new_str = []
|
|
for c in s:
|
|
if c not in " \n\t\"":
|
|
new_str.append(c)
|
|
elif c == '\"' and in_quotes == False:
|
|
in_quotes = True
|
|
elif c == '\"' and in_quotes == True:
|
|
in_quotes = False
|
|
if len(new_str) == 0:
|
|
# don't include things that are set to '""'
|
|
del new_lst[-1]
|
|
elif in_quotes == True: # append ws if in quotes
|
|
new_str.append(c)
|
|
elif len(new_str): # not in quotes, break on ws if anything in new_str
|
|
new_lst.append("".join(new_str))
|
|
new_str = []
|
|
else:
|
|
pass # drop ws
|
|
|
|
return new_lst
|
|
|
|
def parse(txt, cur):
|
|
cur = 0
|
|
end = len(txt) - 1
|
|
while cur != end:
|
|
if txt[cur] == "storage":
|
|
cur, d = parse_storage(txt, cur)
|
|
sos.append(d)
|
|
elif txt[cur] == "fabric":
|
|
cur = parse_fabric(txt, cur)
|
|
|
|
with open("/etc/target/scsi_target.lio") as f:
|
|
txt = f.read()
|
|
txt = split(txt)
|
|
cur = parse(txt, 0)
|
|
|
|
output = dict(storage_objects=sos, fabric_modules=fabs, targets=targs)
|
|
|
|
print(json.dumps(output, indent=2, sort_keys=True))
|
|
|