Merge "Add validation for performance tuning option"
[ta/cm-plugins.git] / validators / src / PerformanceProfilesValidation.py
1 #!/usr/bin/python
2 # Copyright 2019 Nokia
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #    http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 import json
17
18 from cmdatahandlers.api import validation
19 from cmframework.apis import cmvalidator
20
21
22 class PerformanceProfilesValidation(cmvalidator.CMValidator):
23     DOMAIN = 'cloud.performance_profiles'
24     SUBSCRIPTION = r'^cloud\.performance_profiles$'
25
26     HUGEPAGESZ = 'hugepagesz'
27     DEFAULT_HUGEPAGESZ = 'default_hugepagesz'
28     HUGEPAGES = 'hugepages'
29     PLATFORM_CPUS = 'platform_cpus'
30     DPDK_CPUS = 'ovs_dpdk_cpus'
31     CAAS_CPU_POOLS = 'caas_cpu_pools'
32     CAAS_CPU_POOL_ATTRIBUTES = ['exclusive_pool_percentage', 'shared_pool_percentage']
33     CAAS_CPU_POOL_SHARE = 'caas_cpu_pool_share'
34     TUNING = 'tuning'
35
36     NUMA0 = 'numa0'
37     NUMA1 = 'numa1'
38     NUMA_VALUES = [NUMA0, NUMA1]
39
40     HUGEPAGESZ_VALUES = ['2M', '1G']
41     TUNING_OPTIONS = ['low_latency', 'standard']
42
43     INFO_HUGEPAGESZ = 'Valid values: %s' % HUGEPAGESZ_VALUES
44     INFO_HUGEPAGES = 'Must be positive integer'
45     INFO_CPUS = 'Must be zero or positive integer'
46     INFO_PLATFORM_CPUS = 'Platform requires at least one core from NUMA0'
47     INFO_TUNING = 'Valid tuning options are %s' % TUNING_OPTIONS
48
49     ERR_MISSING_DATA = 'Performance profiles validation input does not contain {} data'
50     ERR_INVALID_VALUE = 'Invalid %s value in performance profile {}: %s'
51     ERR_INVALID_CONFIG = 'Invalid {} config (not a dict)'
52
53     ERR_HUGEPAGESZ = ERR_INVALID_VALUE % (HUGEPAGESZ, INFO_HUGEPAGESZ)
54     ERR_DEFAULT_HUGEPAGESZ = ERR_INVALID_VALUE % (DEFAULT_HUGEPAGESZ, INFO_HUGEPAGESZ)
55     ERR_HUGEPAGES = ERR_INVALID_VALUE % (HUGEPAGES, INFO_HUGEPAGES)
56
57     ERR_NUMA = "Invalid NUMA value in performance profile {}"
58     ERR_CPUS = ERR_INVALID_VALUE % ("platform/ovs_dpdk cpu", INFO_CPUS)
59     ERR_PLATFORM_CPUS = ERR_INVALID_VALUE % ("platform_cpus", INFO_PLATFORM_CPUS)
60     ERR_CPU_POOL_RATIO = 'caas_cpu_pools total cpu percentage exceeded'
61     ERR_CAAS_CPU_POOL_TYPE = 'caas_cpu_pools percentage values should be integer'
62     ERR_CAAS_DEFAULT_POOL = 'caas_cpu_pool_share value should be integer between 0 and 100'
63     ERR_TUNING = "Invalid %s value in {}. %s" % (TUNING, INFO_TUNING)
64
65     @staticmethod
66     def raise_error(context, err_type):
67         raise validation.ValidationError(err_type.format(context))
68
69     def get_subscription_info(self):
70         return self.SUBSCRIPTION
71
72     def validate_set(self, props):
73         conf = self.get_conf(props)
74         if isinstance(conf, dict):
75             self.validate(conf)
76         elif conf:
77             self.raise_error(self.DOMAIN, self.ERR_INVALID_CONFIG)
78
79     def get_conf(self, props):
80         if not isinstance(props, dict) or self.DOMAIN not in props:
81             self.raise_error(self.DOMAIN, self.ERR_MISSING_DATA)
82         return json.loads(props[self.DOMAIN])
83
84     def validate(self, conf):
85         for profile, entries in conf.iteritems():
86             if isinstance(entries, dict):
87                 self.validate_profile(profile, entries)
88
89     def validate_profile(self, profile, entries):
90         for key, value in entries.iteritems():
91             self.validate_value(profile, key, value)
92
93     def validate_value(self, profile, key, value):
94         if key == self.HUGEPAGESZ:
95             self.validate_hugepagesz(profile, value)
96         elif key == self.DEFAULT_HUGEPAGESZ:
97             self.validate_default_hugepagesz(profile, value)
98         elif key == self.HUGEPAGES:
99             self.validate_hugepages(profile, value)
100         elif key == self.PLATFORM_CPUS:
101             self.validate_platform_cpus(profile, value)
102         elif key == self.DPDK_CPUS:
103             self.validate_ovs_dpdk_cpus(profile, value)
104         elif key == self.CAAS_CPU_POOLS:
105             self.validate_caas_cpu_pools(profile, value)
106         elif key == self.CAAS_CPU_POOL_SHARE:
107             self.validate_caas_cpu_pool_share(value)
108         elif key == self.TUNING:
109             self.validate_tuning(profile, value)
110
111     def validate_hugepagesz(self, profile, value):
112         if value not in self.HUGEPAGESZ_VALUES:
113             self.raise_error(profile, self.ERR_HUGEPAGESZ)
114
115     def validate_default_hugepagesz(self, profile, value):
116         if value not in self.HUGEPAGESZ_VALUES:
117             self.raise_error(profile, self.ERR_DEFAULT_HUGEPAGESZ)
118
119     def validate_hugepages(self, profile, value):
120         if not (isinstance(value, (int, long)) and value > 0):
121             self.raise_error(profile, self.ERR_HUGEPAGES)
122
123     def validate_numa_names(self, profile, cpus):
124         if isinstance(cpus, dict):
125             for key in cpus.keys():
126                 if key not in self.NUMA_VALUES:
127                     self.raise_error(profile, self.ERR_NUMA)
128
129     def validate_cpu_values(self, profile, cpus):
130         if isinstance(cpus, dict):
131             for value in cpus.values():
132                 if not (isinstance(value, (int, long)) and value >= 0):
133                     self.raise_error(profile, self.ERR_CPUS)
134
135     def validate_platform_cpus(self, profile, cpus):
136         self.validate_numa_names(profile, cpus)
137         if cpus.get(self.NUMA1, None) is not None and cpus.get(self.NUMA0, None) is None:
138             self.raise_error(profile, self.ERR_PLATFORM_CPUS)
139         if cpus.get(self.NUMA1, None) is not None and cpus.get(self.NUMA0, None) == 0:
140             self.raise_error(profile, self.ERR_PLATFORM_CPUS)
141         self.validate_cpu_values(profile, cpus)
142
143     def validate_ovs_dpdk_cpus(self, profile, cpus):
144         self.validate_numa_names(profile, cpus)
145         self.validate_cpu_values(profile, cpus)
146
147     def validate_caas_cpu_pools(self, profile, pools):
148         sum_ratio = 0
149         self.allowed_attributes(profile, pools, self.CAAS_CPU_POOL_ATTRIBUTES)
150         self.is_attribute_present(profile, pools, self.CAAS_CPU_POOL_ATTRIBUTES)
151         for value in pools.values():
152             if not isinstance(value, int) or (value > 100) or (value < 0):
153                 self.raise_error(profile, self.ERR_CAAS_CPU_POOL_TYPE)
154             sum_ratio += value
155         if sum_ratio > 100:
156             self.raise_error(profile, self.ERR_CPU_POOL_RATIO)
157
158     def validate_tuning(self, profile, option):
159         if option not in self.TUNING_OPTIONS:
160             self.raise_error(profile, self.ERR_TUNING)
161
162     def allowed_attributes(self, profile, entries, allowed_attributes):
163         for key in entries.keys():
164             if key not in allowed_attributes:
165                 self.raise_error(profile, 'Attribute %s is not allowed in profile %s, '
166                                  'allowed attributes: \"%s\"' %
167                                  (key, profile, str(",".join(allowed_attributes))))
168
169     def is_attribute_present(self, profile, entries, attributes):
170         is_present = False
171         for key in entries.keys():
172             if key in attributes:
173                 is_present = True
174         if not is_present:
175             self.raise_error(profile, 'Profile: %s should contain at least one of the following '
176                              'attributes: \"%s\"' % (profile, str(",".join(attributes))))
177
178     def validate_caas_cpu_pool_share(self, value):
179         if not isinstance(value, (int)) or (value > 100) or (value < 0):
180             self.raise_error(value, self.ERR_CAAS_DEFAULT_POOL)