| Adam Israel | dcdf82b | 2017-08-15 15:26:43 -0400 | [diff] [blame] | 1 | # |
| 2 | # Module that parses constraints |
| 3 | # |
| 4 | # The current version of juju core expects the client to take |
| 5 | # constraints given in the form "mem=10G foo=bar" and parse them into |
| 6 | # json that looks like {"mem": 10240, "foo": "bar"}. This module helps us |
| 7 | # accomplish that task. |
| 8 | # |
| 9 | # We do not attempt to duplicate the checking done in |
| 10 | # client/_client.py:Value here. That class will verify that the |
| 11 | # constraints keys are valid, and that we can successfully dump the |
| 12 | # constraints dict to json. |
| 13 | # |
| 14 | # Once https://bugs.launchpad.net/juju/+bug/1645402 is addressed, this |
| 15 | # module should be deprecated. |
| 16 | # |
| 17 | |
| 18 | import re |
| 19 | |
| 20 | # Matches on a string specifying memory size |
| 21 | MEM = re.compile('^[1-9][0-9]*[MGTP]$') |
| 22 | |
| 23 | # Multiplication factors to get Megabytes |
| 24 | # https://github.com/juju/juju/blob/master/constraints/constraints.go#L666 |
| 25 | FACTORS = { |
| 26 | "M": 1, |
| 27 | "G": 1024, |
| 28 | "T": 1024 * 1024, |
| 29 | "P": 1024 * 1024 * 1024 |
| 30 | } |
| 31 | |
| Adam Israel | b094366 | 2018-08-02 15:32:00 -0400 | [diff] [blame] | 32 | LIST_KEYS = {'tags', 'spaces'} |
| 33 | |
| Adam Israel | dcdf82b | 2017-08-15 15:26:43 -0400 | [diff] [blame] | 34 | SNAKE1 = re.compile(r'(.)([A-Z][a-z]+)') |
| 35 | SNAKE2 = re.compile('([a-z0-9])([A-Z])') |
| 36 | |
| 37 | |
| 38 | def parse(constraints): |
| 39 | """ |
| 40 | Constraints must be expressed as a string containing only spaces |
| 41 | and key value pairs joined by an '='. |
| 42 | |
| 43 | """ |
| 44 | if not constraints: |
| 45 | return None |
| 46 | |
| 47 | if type(constraints) is dict: |
| 48 | # Fowards compatibilty: already parsed |
| 49 | return constraints |
| 50 | |
| 51 | constraints = { |
| Adam Israel | b094366 | 2018-08-02 15:32:00 -0400 | [diff] [blame] | 52 | normalize_key(k): ( |
| 53 | normalize_list_value(v) if k in LIST_KEYS else |
| 54 | normalize_value(v) |
| 55 | ) for k, v in [s.split("=") for s in constraints.split(" ")]} |
| Adam Israel | dcdf82b | 2017-08-15 15:26:43 -0400 | [diff] [blame] | 56 | |
| 57 | return constraints |
| 58 | |
| 59 | |
| 60 | def normalize_key(key): |
| 61 | key = key.strip() |
| 62 | |
| 63 | key = key.replace("-", "_") # Our _client lib wants "_" in place of "-" |
| 64 | |
| 65 | # Convert camelCase to snake_case |
| 66 | key = SNAKE1.sub(r'\1_\2', key) |
| 67 | key = SNAKE2.sub(r'\1_\2', key).lower() |
| 68 | |
| 69 | return key |
| 70 | |
| 71 | |
| 72 | def normalize_value(value): |
| 73 | value = value.strip() |
| 74 | |
| 75 | if MEM.match(value): |
| 76 | # Translate aliases to Megabytes. e.g. 1G = 10240 |
| 77 | return int(value[:-1]) * FACTORS[value[-1:]] |
| 78 | |
| Adam Israel | dcdf82b | 2017-08-15 15:26:43 -0400 | [diff] [blame] | 79 | if value.isdigit(): |
| 80 | return int(value) |
| 81 | |
| 82 | return value |
| Adam Israel | b094366 | 2018-08-02 15:32:00 -0400 | [diff] [blame] | 83 | |
| 84 | |
| 85 | def normalize_list_value(value): |
| 86 | values = value.strip().split(',') |
| 87 | return [normalize_value(value) for value in values] |