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))
|
|
|