From: Kushal Deb Date: Wed, 30 Oct 2024 15:45:57 +0000 (+0530) Subject: Fix tuned profile to allow multiple settings X-Git-Tag: v20.0.0~646^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=db943eef653ecfd28700cd64a0751f6efe1ab807;p=ceph.git Fix tuned profile to allow multiple settings Signed-off-by: Kushal Deb --- diff --git a/src/pybind/mgr/cephadm/inventory.py b/src/pybind/mgr/cephadm/inventory.py index 8a16ef8ae80c..c30512407136 100644 --- a/src/pybind/mgr/cephadm/inventory.py +++ b/src/pybind/mgr/cephadm/inventory.py @@ -637,6 +637,9 @@ class TunedProfileStore(): logger.error( f'Attempted to set setting "{setting}" for nonexistent os tuning profile "{profile}"') + def add_settings(self, profile: str, settings: dict) -> None: + self.process_settings(profile, settings, action='add') + def rm_setting(self, profile: str, setting: str) -> None: if profile in self.profiles: if setting in self.profiles[profile].settings: @@ -650,6 +653,39 @@ class TunedProfileStore(): logger.error( f'Attempted to remove setting "{setting}" from nonexistent os tuning profile "{profile}"') + def rm_settings(self, profile: str, settings: List[str]) -> None: + self.process_settings(profile, settings, action='remove') + + def process_settings(self, profile: str, settings: Union[dict, list], action: str) -> None: + """ + Process settings by either adding or removing them based on the action specified. + """ + if profile not in self.profiles: + logger.error(f'Attempted to {action} settings for nonexistent os tuning profile "{profile}"') + return + profile_settings = self.profiles[profile].settings + if action == 'remove' and isinstance(settings, list): + invalid_settings = [s for s in settings if '=' in s or s not in profile_settings] + if invalid_settings: + raise OrchestratorError( + f"Invalid settings: {', '.join(invalid_settings)}. " + "Ensure settings are specified without '=' and exist in the profile. Correct format: key1,key2" + ) + if action == 'add' and isinstance(settings, dict): + for setting, value in settings.items(): + self.profiles[profile].settings[setting] = value + elif action == 'remove' and isinstance(settings, list): + for setting in settings: + self.profiles[profile].settings.pop(setting, '') + else: + logger.error( + f'Invalid action "{action}" for settings modification for tuned profile ' + f'"{profile}". Valid actions are "add" and "remove"' + ) + return + self.profiles[profile]._last_updated = datetime_to_str(datetime_now()) + self.save() + def add_profile(self, spec: TunedProfileSpec) -> None: spec._last_updated = datetime_to_str(datetime_now()) self.profiles[spec.profile_name] = spec diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py index 422db9d64ea7..d050eaac1392 100644 --- a/src/pybind/mgr/cephadm/module.py +++ b/src/pybind/mgr/cephadm/module.py @@ -3498,6 +3498,33 @@ Then run the following: self._kick_serve_loop() return f'Added setting {setting} with value {value} to tuned profile {profile_name}' + @handle_orch_error + def tuned_profile_add_settings(self, profile_name: str, settings: dict) -> str: + if profile_name not in self.tuned_profiles: + raise OrchestratorError( + f"Tuned profile {profile_name} does not exist. Cannot add setting." + ) + self.tuned_profiles.add_settings(profile_name, settings) + results = [ + f"Added setting {key} with value {value} to tuned profile {profile_name}" + for key, value in settings.items() + ] + self._kick_serve_loop() + return "\n".join(results) + + @handle_orch_error + def tuned_profile_rm_settings(self, profile_name: str, settings: List[str]) -> str: + if profile_name not in self.tuned_profiles: + raise OrchestratorError( + f"Tuned profile {profile_name} does not exist. Cannot remove setting." + ) + self.tuned_profiles.rm_settings(profile_name, settings) + results = [ + f'Removed setting {settings} from tuned profile {profile_name}' + ] + self._kick_serve_loop() + return "\n".join(results) + @handle_orch_error def tuned_profile_rm_setting(self, profile_name: str, setting: str) -> str: if profile_name not in self.tuned_profiles: diff --git a/src/pybind/mgr/orchestrator/_interface.py b/src/pybind/mgr/orchestrator/_interface.py index efe0de55bd07..a505801eea5e 100644 --- a/src/pybind/mgr/orchestrator/_interface.py +++ b/src/pybind/mgr/orchestrator/_interface.py @@ -901,10 +901,18 @@ class Orchestrator(object): """Change/Add a specific setting for a tuned profile""" raise NotImplementedError() + def tuned_profile_add_settings(self, profile_name: str, setting: dict) -> OrchResult[str]: + """Change/Add multiple settings for a tuned profile""" + raise NotImplementedError() + def tuned_profile_rm_setting(self, profile_name: str, setting: str) -> OrchResult[str]: """Remove a specific setting for a tuned profile""" raise NotImplementedError() + def tuned_profile_rm_settings(self, profile_name: str, settings: List[str]) -> OrchResult[str]: + """Remove multiple settings from a tuned profile""" + raise NotImplementedError + def upgrade_check(self, image: Optional[str], version: Optional[str]) -> OrchResult[str]: raise NotImplementedError() diff --git a/src/pybind/mgr/orchestrator/module.py b/src/pybind/mgr/orchestrator/module.py index fdb9280d7ed9..332bc75d862e 100644 --- a/src/pybind/mgr/orchestrator/module.py +++ b/src/pybind/mgr/orchestrator/module.py @@ -2250,6 +2250,39 @@ Usage: res = raise_if_exception(completion) return HandleCommandResult(stdout=res) + @_cli_write_command("orch tuned-profile add-settings") + def _tuned_profile_add_settings(self, profile_name: str, settings: str) -> HandleCommandResult: + try: + setting_pairs = settings.split(",") + parsed_setting = {} + parsed_setting = {key.strip(): value.strip() for key, value in (s.split('=', 1) for s in setting_pairs)} + completion = self.tuned_profile_add_settings(profile_name, parsed_setting) + res = raise_if_exception(completion) + return HandleCommandResult(stdout=res) + except ValueError: + error_message = ( + "Error: Invalid format detected. " + "The correct format is key=value pairs separated by commas," + "e.g., 'vm.swappiness=11,vm.user_reserve_kbytes=116851'" + ) + return HandleCommandResult(stderr=error_message) + + @_cli_write_command("orch tuned-profile rm-settings") + def _tuned_profile_rm_settings(self, profile_name: str, settings: str) -> HandleCommandResult: + try: + setting = [s.strip() for s in settings.split(",") if s.strip()] + if not setting: + raise ValueError( + "Error: Invalid format." + "The correct format is key1,key2" + "e.g., vm.swappiness,vm.user_reserve_kbytes" + ) + completion = self.tuned_profile_rm_settings(profile_name, setting) + res = raise_if_exception(completion) + return HandleCommandResult(stdout=res) + except ValueError as e: + return HandleCommandResult(stderr=str(e)) + def self_test(self) -> None: old_orch = self._select_orchestrator() self._set_backend('')