Skip to content

Commit d00e90b

Browse files
authored
Consistent return/yield statements in generators (kadalu#647)
* Kubectl kadalu might need to be compatible with Py2 as well Signed-off-by: Leela Venkaiah G <[email protected]>
1 parent 382c527 commit d00e90b

File tree

5 files changed

+78
-42
lines changed

5 files changed

+78
-42
lines changed

Makefile

+10-8
Original file line numberDiff line numberDiff line change
@@ -70,25 +70,27 @@ gen-manifest:
7070
@echo "kubectl apply -f manifests/kadalu-operator-rke.yaml"
7171
@echo "kubectl apply -f manifests/csi-nodeplugin-rke.yaml"
7272

73+
# W0511: TODO Statements
74+
# W1514: Using 'open' with default encoding (in our usage it shouldn't matter)
7375
pylint:
7476
@cp lib/kadalulib.py csi/
7577
@cp lib/kadalulib.py server/
7678
@cp lib/kadalulib.py operator/
7779
@cp cli/kubectl_kadalu/utils.py operator/
7880
@cp server/kadalu_quotad/quotad.py server/kadalu_quotad/glusterutils.py server/
7981
@pylint --disable=W0511 -s n lib/kadalulib.py
80-
@pylint --disable=W0511 -s n server/glusterfsd.py
81-
@pylint --disable W0511,W0603 -s n server/quotad.py
82+
@pylint --disable=W0511,W1514 -s n server/glusterfsd.py
83+
@pylint --disable W0511,W0603,W1514 -s n server/quotad.py
8284
@pylint --disable=W0511 -s n server/server.py
83-
@pylint --disable=W0511 -s n server/shd.py
84-
@pylint --disable=W0603 -s n server/glusterutils.py
85-
@pylint --disable=W0511,R0911,W0603 -s n csi/controllerserver.py
85+
@pylint --disable=W0511,W1514 -s n server/shd.py
86+
@pylint --disable=W0603,W1514 -s n server/glusterutils.py
87+
@pylint --disable=W0511,R0911,W0603,W1514 -s n csi/controllerserver.py
8688
@pylint --disable=W0511 -s n csi/identityserver.py
8789
@pylint --disable=W0511,R1732 -s n csi/main.py
8890
@pylint --disable=W0511 -s n csi/nodeserver.py
89-
@pylint --disable=W0511,C0302,R0912 -s n csi/volumeutils.py
90-
@pylint --disable=W0511,C0302 -s n operator/main.py
91-
@pylint --disable=W0511 -s n extras/scripts/gen_manifest.py
91+
@pylint --disable=W0511,C0302,R0912,W1514,R1710 -s n csi/volumeutils.py
92+
@pylint --disable=W0511,C0302,W1514 -s n operator/main.py
93+
@pylint --disable=W0511,W1514 -s n extras/scripts/gen_manifest.py
9294
@rm csi/kadalulib.py
9395
@rm server/kadalulib.py
9496
@rm operator/kadalulib.py

cli/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pytest:
1414
${PYTHON} -m pytest kubectl_kadalu
1515

1616
pylint:
17-
cd kubectl_kadalu && ${PYTHON} -m pylint --disable W0511,R0801 *.py
17+
cd kubectl_kadalu && ${PYTHON} -m pylint --disable W0511,R0801,W1406 *.py
1818

1919
mypy:
2020
cd kubectl_kadalu && ${PYTHON} -m mypy *.py

csi/controllerserver.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -496,9 +496,8 @@ def ListVolumes(self, request, context):
496496
# Run and wait for 'send'
497497
try:
498498
next(GEN)
499-
except StopIteration:
499+
except StopIteration as errmsg:
500500
# Handle no PVC created from a storage volume yet
501-
errmsg = "No PVC found in any hostvol"
502501
logging.error(errmsg)
503502
context.set_details(errmsg)
504503
context.set_code(grpc.StatusCode.ABORTED)
@@ -507,17 +506,16 @@ def ListVolumes(self, request, context):
507506
try:
508507
# Get list of PVCs limited at max_entries by suppling the token
509508
pvcs, next_token = GEN.send(starting_token)
510-
except StopIteration:
511-
errmsg = "Runtime Error while getting PVCs list"
509+
except StopIteration as errmsg:
512510
logging.error(errmsg)
513511
context.set_details(errmsg)
514512
context.set_code(grpc.StatusCode.ABORTED)
515513
return csi_pb2.ListVolumesResponse()
516514

517515
entries = [{
518516
"volume": {
519-
"volume_id": value[0],
520-
"capacity_bytes": value[1]
517+
"volume_id": value.get("name"),
518+
"capacity_bytes": value.get("size"),
521519
}
522520
} for value in pvcs]
523521

csi/exporter.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,13 @@ def collect(self):
8686

8787
# Gathers capacity metrics for each subvol
8888
for pvc in yield_pvc_from_mntdir(os.path.join(pth, "info")):
89-
# pvc[0]: name
90-
# pvc[1]: size
91-
# pvc[2]: path prefix
92-
pvcpath = os.path.join(pth, pvc[2])
93-
pvcname = pvc[0]
94-
pvclabels = labels + [pvcname]
95-
96-
stat = os.statvfs(pvcpath)
89+
if pvc is None:
90+
continue
91+
pvcpath_full = os.path.join(pth, pvc.get("path_prefix"),
92+
pvc.get("name"))
93+
pvclabels = labels + [pvc.get("name")]
94+
95+
stat = os.statvfs(pvcpath_full)
9796

9897
# Capacity
9998
total = stat.f_bsize * stat.f_blocks

csi/volumeutils.py

+56-19
Original file line numberDiff line numberDiff line change
@@ -1197,9 +1197,17 @@ def check_external_volume(pv_request, host_volumes):
11971197
return hvol
11981198

11991199

1200+
# Methods starting with 'yield_*' upon not a single entry raise StopIteration
1201+
# (via return "reason") and upon no entry for a specific scenario yields
1202+
# None. Caller should handle None gracefully based on the context the info is
1203+
# required, like:
1204+
# 1. Is it critical enough to serve the storage to user? Fail fast
1205+
# 2. Performing health checks or which can be eventually consistent (listvols)?
1206+
# Handle gracefully
12001207
def yield_hostvol_mount():
12011208
"""Yields mount directory where hostvol is mounted"""
12021209
host_volumes = get_pv_hosting_volumes()
1210+
info_exist = False
12031211
for volume in host_volumes:
12041212
hvol = volume['name']
12051213
mntdir = os.path.join(HOSTVOL_MOUNTDIR, hvol)
@@ -1208,19 +1216,33 @@ def yield_hostvol_mount():
12081216
except CommandException as excep:
12091217
logging.error(
12101218
logf("Unable to mount volume", hvol=hvol, excep=excep.args))
1211-
return
1219+
# We aren't able to mount this specific hostvol
1220+
yield None
12121221
logging.info(logf("Volume is mounted successfully", hvol=hvol))
1213-
# After mounting a hostvol, start looking for PVC from '/mntdir/info'
1214-
yield os.path.join(mntdir, 'info')
1222+
info_path = os.path.join(mntdir, 'info')
1223+
if os.path.isdir(info_path):
1224+
# After mounting a hostvol, start looking for PVC from '/mnt/<pool>/info'
1225+
info_exist = True
1226+
yield info_path
1227+
if not info_exist:
1228+
# A generator should yield "something", to signal "StopIteration" if
1229+
# there's no info file on any pool, there should be empty yield
1230+
# Note: raise StopIteration =~ return, but return with a reason is
1231+
# better.
1232+
return "No storage pool exists"
12151233

12161234

12171235
def yield_pvc_from_mntdir(mntdir):
12181236
"""Yields PVCs from a single mntdir"""
1219-
# Max recursion depth is two subdirs (/<mntdir>/x/y/<pvc-hash-.json>)
1220-
# If 'subvol' exist then max depth will be three subdirs
1237+
# Max recursion depth is two subdirs (/<mntdir>/x/y/<pvc-name.json>)
1238+
# If 'subvol/virtblock/rawblock' exist then max depth will be three subdirs
1239+
if mntdir.endswith("info") and not os.path.isdir(mntdir):
1240+
# There might be a chance that this function is used standalone and so
1241+
# check for 'info' directory exists
1242+
yield None
12211243
for child in os.listdir(mntdir):
12221244
name = os.path.join(mntdir, child)
1223-
if os.path.isdir(name):
1245+
if os.path.isdir(name) and len(os.listdir(name)):
12241246
yield from yield_pvc_from_mntdir(name)
12251247
elif name.endswith('json'):
12261248
# Base case we are interested in, the filename ending with '.json'
@@ -1230,15 +1252,27 @@ def yield_pvc_from_mntdir(mntdir):
12301252
data = json.loads(handle.read().strip())
12311253
logging.debug(
12321254
logf("Found a PVC at", path=file_path, size=data.get("size")))
1233-
yield name[name.find("pvc"):name.find(".json")], data.get("size"), \
1234-
data.get("path_prefix")
1255+
data["name"] = name[name.rfind("/") + 1:name.rfind(".json")]
1256+
yield data
1257+
else:
1258+
# If leaf is neither a json file nor a directory with contents
1259+
yield None
12351260

12361261

12371262
def yield_pvc_from_hostvol():
12381263
"""Yields a single PVC sequentially from all the hostvolumes"""
1264+
pvc_exist = False
12391265
for mntdir in yield_hostvol_mount():
1240-
pvcs = yield_pvc_from_mntdir(mntdir)
1241-
yield from pvcs
1266+
if mntdir is not None:
1267+
# Only yield PVC if we are able to mount corresponding pool
1268+
pvc = yield_pvc_from_mntdir(mntdir)
1269+
for data in pvc:
1270+
if data is not None:
1271+
pvc_exist = True
1272+
data["mntdir"] = os.path.dirname(mntdir.strip("/"))
1273+
yield data
1274+
if not pvc_exist:
1275+
return "No PVC exist in any storage pool"
12421276

12431277

12441278
def wrap_pvc(pvc_gen):
@@ -1249,18 +1283,19 @@ def wrap_pvc(pvc_gen):
12491283
gen = pvc_gen()
12501284
try:
12511285
prev = next(gen)
1252-
except StopIteration:
1253-
return
1254-
for value in gen:
1255-
yield prev, False
1256-
prev = value
1257-
yield prev, True
1258-
1286+
for value in gen:
1287+
yield prev, False
1288+
prev = value
1289+
yield prev, True
1290+
except StopIteration as errmsg:
1291+
return errmsg
12591292

12601293
def yield_list_of_pvcs(max_entries=0):
12611294
"""Yields list of PVCs limited at 'max_entries'"""
1262-
# List of tuples containing PVC Name and Size
1295+
# List of dicts containing data of PVC from info_file (with extra keys,
1296+
# 'name', 'mntdir')
12631297
pvcs = []
1298+
idx = -1
12641299
for idx, value in enumerate(wrap_pvc(yield_pvc_from_hostvol)):
12651300
pvc, last = value
12661301
token = "" if last else str(idx)
@@ -1279,10 +1314,12 @@ def yield_list_of_pvcs(max_entries=0):
12791314
logging.debug(logf("Received token", next_token=next_token))
12801315
if next_token and not last and (int(next_token) !=
12811316
int(token) - max_entries):
1282-
return
1317+
return "Incorrect token supplied"
12831318
logging.debug(
12841319
logf("Yielding PVC set and next token is ",
12851320
token=token,
12861321
pvcs=pvcs))
12871322
yield pvcs, token
12881323
pvcs *= 0
1324+
if idx == -1:
1325+
return "No PVC exist in any storage pool"

0 commit comments

Comments
 (0)