Cinder - easy to hit RecursionError with complex config formulas

bartosz.rabiega at ovhcloud.com bartosz.rabiega at ovhcloud.com
Mon Mar 28 09:20:28 UTC 2022


Hello,

I wanted to share something I've recently discovered - there is a 
strange issue with cinder evaluating complex configuration formulas.

For example when using complex goodness_function it's quite easy to hit:
     ERROR cinder.scheduler.weights.goodness maximum recursion depth 
exceeded: RecursionError: maximum recursion depth exceeded

I did some tests to find the root cause. It seems that pyparsing module 
does deep recursive parsing quite a lot (default recursion limit is 1000).

Not sure how to solve it but it would be nice to at least have a warning 
in the docs.
Results and methodology below.

Tests were done using following code snippet
-------------------------------------------
from cinder.scheduler.evaluator.evaluator import evaluate
import sys

formulas = [
     "1",
     "1 + 1",
     "max(1, 2, 3)",
     "max(1 + (10 / 20), 2, 3)",
     "(1 + max(1 + (10 / 20), 2, 3)) / 100",
     "((1 + max(1 + (10 / 20), 2, 3)) / 100) + stats.total_capacity_gb",
     "(((1 + max(1 + (10 / 20), 2, 3)) / 100) + stats.total_capacity_gb) 
/ 100",
]

stats = {
     'allocated_capacity_gb': 50000,
     'total_capacity_gb': 60000,
     'free_capacity_gb': 10000,
}

for f in formulas:
     for i in range(10, 2000, 10):
         sys.setrecursionlimit(i)
         try:
             res = evaluate(f, stats=stats)
             print(f"{i}: {f}")
             break
         except RecursionError:
             pass


Test results
--------------------------------
Cinder 14.3.1, pyparsing<3.0
210: 1
210: 1 + 1
450: max(1, 2, 3)
600: max(1 + (10 / 20), 2, 3)
790: (1 + max(1 + (10 / 20), 2, 3)) / 100
980: ((1 + max(1 + (10 / 20), 2, 3)) / 100) + stats.total_capacity_gb
1160: (((1 + max(1 + (10 / 20), 2, 3)) / 100) + stats.total_capacity_gb) 
/ 100

Cinder 16.4.2, pyparsing<3.0
220: 1
220: 1 + 1
450: max(1, 2, 3)
610: max(1 + (10 / 20), 2, 3)
790: (1 + max(1 + (10 / 20), 2, 3)) / 100
980: ((1 + max(1 + (10 / 20), 2, 3)) / 100) + stats.total_capacity_gb
1170: (((1 + max(1 + (10 / 20), 2, 3)) / 100) + stats.total_capacity_gb) 
/ 100

Cinder 19.1.0, pyparsing<3.0
220: 1
220: 1 + 1
450: max(1, 2, 3)
610: max(1 + (10 / 20), 2, 3)
790: (1 + max(1 + (10 / 20), 2, 3)) / 100
980: ((1 + max(1 + (10 / 20), 2, 3)) / 100) + stats.total_capacity_gb
1170: (((1 + max(1 + (10 / 20), 2, 3)) / 100) + stats.total_capacity_gb) 
/ 100


BR



More information about the openstack-discuss mailing list