Add tuning option to performance profiles
[ta/config-manager.git] / cmdatahandlers / src / cmdatahandlers / performance_profiles / config.py
1 # Copyright 2019 Nokia
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 from cmdatahandlers.api import configerror
16 from cmdatahandlers.api import config
17 from cmdatahandlers.api import utils
18
19
20 class Config(config.Config):
21
22     DEFAULT_HUGEPAGESZ = 'default_hugepagesz'
23     HUGEPAGESZ = 'hugepagesz'
24     HUGEPAGES = 'hugepages'
25     PLATFORM_CPUS = 'platform_cpus'
26     OVS_DPDK_CPUS = 'ovs_dpdk_cpus'
27     TUNING = 'tuning'
28     LOW_LATENCY_OPTIONS = 'low_latency_options'
29     PROFILE_OPTIONS = {DEFAULT_HUGEPAGESZ: 'get_profile_default_hugepage_size',
30                        HUGEPAGESZ: 'get_profile_hugepage_size',
31                        HUGEPAGES: 'get_profile_hugepage_count',
32                        PLATFORM_CPUS: 'get_platform_cpus',
33                        OVS_DPDK_CPUS: 'get_ovs_dpdk_cpus',
34                        TUNING: 'get_tuning',
35                        LOW_LATENCY_OPTIONS: 'get_low_latency_kcmd_options'}
36
37     ERR_INVALID_PROFILE = 'Invalid profile name {}'
38     ERR_MISSING_PROFILE_KEY = 'Profile {} does not have %s'
39     ERR_MISSING_DEFAULT_HUGEPAGESZ = ERR_MISSING_PROFILE_KEY % DEFAULT_HUGEPAGESZ
40     ERR_MISSING_HUGEPAGESZ = ERR_MISSING_PROFILE_KEY % HUGEPAGESZ
41     ERR_MISSING_HUGEPAGES = ERR_MISSING_PROFILE_KEY % HUGEPAGES
42     ERR_MISSING_PLATFORM_CPUS = ERR_MISSING_PROFILE_KEY % PLATFORM_CPUS
43     ERR_MISSING_OVS_DPDK_CPUS = ERR_MISSING_PROFILE_KEY % OVS_DPDK_CPUS
44     ERR_MISSING_TUNING = ERR_MISSING_PROFILE_KEY % TUNING
45     ERR_INVALID_PROFILE_KEY = 'Profile {} got invalid option %s'
46     ERR_INVALID_LOW_LATENCY_OPTIONS = ERR_INVALID_PROFILE_KEY % LOW_LATENCY_OPTIONS
47
48     @staticmethod
49     def raise_error(context, err_type):
50         raise configerror.ConfigError(err_type.format(context))
51
52     def __init__(self, confman):
53         super(Config, self).__init__(confman)
54         self.ROOT = 'cloud.performance_profiles'
55         self.DOMAIN = 'performance_profiles'
56
57     def init(self):
58         pass
59
60     def validate(self):
61         self.validate_root()
62         self._validate_performance_profiles()
63
64     def _validate_performance_profiles(self):
65         profiles = self.get_performance_profiles()
66         utils.validate_list_items_unique(profiles)
67         for profile in profiles:
68             self._validate_performance_profile(profile)
69
70     def _validate_performance_profile(self, profile):
71         self.get_profile_default_hugepage_size(profile)
72         self.get_profile_hugepage_size(profile)
73         self.get_profile_hugepage_count(profile)
74         self.get_platform_cpus(profile)
75         self.get_ovs_dpdk_cpus(profile)
76         self._validate_mandatory_option(profile, self.TUNING)
77
78     def _validate_mandatory_option(self, profile, option):
79         if getattr(self, self.PROFILE_OPTIONS[option])(profile) is None:
80             self.raise_error(profile, self.ERR_MISSING_PROFILE_KEY % option)
81
82     def is_valid_profile(self, profile):
83         profiles = self.get_performance_profiles()
84         if profile not in profiles:
85             self.raise_error(profile, self.ERR_INVALID_PROFILE)
86
87     def get_performance_profiles(self):
88         """ get the performance profiles list
89
90             Return:
91
92             A list of performance profile(s) names
93
94             Raise:
95
96             ConfigError in-case of an error
97         """
98         self.validate_root()
99         return self.config[self.ROOT].keys()
100
101     # pylint: disable=invalid-name
102     def get_profile_default_hugepage_size(self, profile):
103         """ get the default hugepage size
104
105             Argument:
106
107             profile name
108
109             Return:
110
111             The default hugepage size
112
113             Raise:
114
115             ConfigError in-case of an error
116         """
117         self.is_valid_profile(profile)
118
119         if self.DEFAULT_HUGEPAGESZ not in self.config[self.ROOT][profile]:
120             self.raise_error(profile, self.ERR_MISSING_DEFAULT_HUGEPAGESZ)
121
122         return self.config[self.ROOT][profile][self.DEFAULT_HUGEPAGESZ]
123
124     def get_profile_hugepage_size(self, profile):
125         """ get the hugepage size
126
127             Argument:
128
129             profile name
130
131             Return:
132
133             The hugepage size
134
135             Raise:
136
137             ConfigError in-case of an error
138         """
139         self.is_valid_profile(profile)
140
141         if self.HUGEPAGESZ not in self.config[self.ROOT][profile]:
142             self.raise_error(profile, self.ERR_MISSING_HUGEPAGESZ)
143
144         return self.config[self.ROOT][profile][self.HUGEPAGESZ]
145
146     def get_profile_hugepage_count(self, profile):
147         """ get the hugepage count
148
149             Argument:
150
151             profile name
152
153             Return:
154
155             The hugepage count
156
157             Raise:
158
159             ConfigError in-case of an error
160         """
161         self.is_valid_profile(profile)
162
163         if self.HUGEPAGES not in self.config[self.ROOT][profile]:
164             self.raise_error(profile, self.ERR_MISSING_HUGEPAGES)
165
166         return self.config[self.ROOT][profile][self.HUGEPAGES]
167
168     def get_platform_cpus(self, profile):
169         """ get the Platforma CPUs (isolate CPUs from the general scheduler).
170
171             Argument:
172
173             profile name
174
175             Return:
176
177             The platform CPUs dictionary
178
179             Raise:
180
181             ConfigError in-case of an error
182         """
183         self.is_valid_profile(profile)
184
185         if self.PLATFORM_CPUS not in self.config[self.ROOT][profile]:
186             self.raise_error(profile, self.ERR_MISSING_PLATFORM_CPUS)
187
188         return self.config[self.ROOT][profile][self.PLATFORM_CPUS]
189
190     def get_ovs_dpdk_cpus(self, profile):
191         """ get the ovs-dpdk cpu(s)
192
193             Argument:
194
195             profile name
196
197             Return:
198
199             The ovs-dpdk dedicated cpu(s) string
200
201             Raise:
202
203             ConfigError in-case of an error
204         """
205         self.is_valid_profile(profile)
206
207         if self.OVS_DPDK_CPUS not in self.config[self.ROOT][profile]:
208             self.raise_error(profile, self.ERR_MISSING_OVS_DPDK_CPUS)
209
210         return self.config[self.ROOT][profile][self.OVS_DPDK_CPUS]
211
212     def get_tuning(self, profile):
213         """ get performance tuning option
214
215             Parameters
216             ----------
217             profile_name : str
218                            Performance profile name.
219             Returns
220             -------
221             str
222                 Performance tuning option.
223
224             Raises
225             ------
226             ConfigError
227         """
228         self.is_valid_profile(profile)
229         if self.TUNING not in self.config[self.ROOT][profile]:
230             return None
231
232         return self.config[self.ROOT][profile][self.TUNING]
233
234     def get_low_latency_kcmd_options(self, profile):
235         """ get low latency kernel cmd option
236
237             Parameters
238             ----------
239             profile_name : str
240                            Performance profile name.
241             Returns
242             -------
243             str
244                 Low latency kernel cmd options.
245
246             Raises
247             ------
248             ConfigError
249         """
250         self.is_valid_profile(profile)
251         if self.LOW_LATENCY_OPTIONS not in self.config[self.ROOT][profile]:
252             return None
253
254         return self.config[self.ROOT][profile][self.LOW_LATENCY_OPTIONS]
255
256     def set_low_latency_kcmd_options(self, profile, options):
257         """ set low latency kernel cmd option
258
259             Parameters
260             ----------
261             profile_name : str
262                            Performance profile name.
263             Returns
264             -------
265             str
266                 Low latency kernel cmd options.
267
268             Raises
269             ------
270             ConfigError
271         """
272         self.is_valid_profile(profile)
273         if not isinstance(options, list):
274             self.raise_error(profile, self.ERR_INVALID_LOW_LATENCY_OPTIONS)
275
276         self._fill_option_value(self.config[self.ROOT][profile], profile, self.LOW_LATENCY_OPTIONS, options)
277
278     def dump(self):
279         """ Dump all performaceprofiles data. """
280
281         self.validate_root()
282         return self.config[self.ROOT]
283
284     def _fill_option_value(self, profile_data, profile, option, value):
285         if value is None:
286             try:
287                 value = getattr(self, self.PROFILE_OPTIONS[option])(profile)
288             except configerror.ConfigError:
289                 return
290         profile_data.update({option:value})
291
292     # pylint: disable=too-many-arguments
293     def update(self, name, platform_cpus=None, ovs_dpdk_cpus=None, hugepages=None,
294                default_hugepagesz=None, hugepagesz=None, tuning=None):
295         """ Update performance profile, overwriting existing profile.
296
297             Parameters
298             ----------
299             name : str
300                    profile name.
301             platform_cpus : dict, optional
302                        Platform CPUs.
303                        The syntax is: {'numa0': <int>, 'numa1': <int>, ..., 'numaN': <int>}
304             ovs_dpdk_cpus : dict, optional
305                         OVS-DPDK dedicated cores.
306                         The syntax is the same as platform_cpus.
307             hugepages : int, optional
308                         The number of allocated persistent huge pages.
309             default_hugepagesz : str, optional
310                                  Default huge page size (the default value is '1G').
311                                  Valid values are '2M' and '1G'
312             hugepagesz : str, optional
313                          Huge page size (the default value is '1G').
314                          Valid values are '2M' and '1G'
315             tuning : str, optional
316                      Performance tuning option.
317                      Valid values are 'low_latency and 'standard' (default).
318         """
319         data = {}
320         self._fill_option_value(data, name, 'platform_cpus', platform_cpus)
321         self._fill_option_value(data, name, 'ovs_dpdk_cpus', ovs_dpdk_cpus)
322         self._fill_option_value(data, name, 'hugepages', hugepages)
323         self._fill_option_value(data, name, 'default_hugepagesz', default_hugepagesz)
324         self._fill_option_value(data, name, 'hugepagesz', hugepagesz)
325         self._fill_option_value(data, name, 'tuning', tuning)
326         self.config[self.ROOT].update({name:data})
327
328     def delete(self, name):
329         """ Remove profile.
330
331             Parametes
332             ---------
333             name : str
334                    profile name.
335
336             Raises
337             ------
338             Raises ConfigError if profile does not exist.
339         """
340         try:
341             self.config[self.ROOT].pop(name)
342         except KeyError:
343             self.raise_error(name, self.ERR_INVALID_PROFILE)