| # Copyright (c) 2016 Cisco and/or its affiliates. |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at: |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """VPP Grub Utility Library.""" |
| |
| import re |
| |
| from vpplib.VPPUtil import VPPUtil |
| |
| __all__ = ["VppGrubUtil"] |
| |
| |
| class VppGrubUtil(object): |
| """VPP Grub Utilities.""" |
| |
| def _get_current_cmdline(self): |
| """ |
| Using /proc/cmdline return the current grub cmdline |
| |
| :returns: The current grub cmdline |
| :rtype: string |
| """ |
| |
| # Get the memory information using /proc/meminfo |
| cmd = "sudo cat /proc/cmdline" |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError( |
| "{} on node {} {} {}".format(cmd, self._node["host"], stdout, stderr) |
| ) |
| |
| self._current_cmdline = stdout.strip("\n") |
| |
| def _get_default_cmdline(self): |
| """ |
| Using /etc/default/grub return the default grub cmdline |
| |
| :returns: The default grub cmdline |
| :rtype: string |
| """ |
| |
| # Get the default grub cmdline |
| rootdir = self._node["rootdir"] |
| gfile = self._node["cpu"]["grub_config_file"] |
| grubcmdline = self._node["cpu"]["grubcmdline"] |
| cmd = "cat {}".format(rootdir + gfile) |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError( |
| "{} Executing failed on node {} {}".format( |
| cmd, self._node["host"], stderr |
| ) |
| ) |
| |
| # Get the Default Linux command line, ignoring commented lines |
| lines = stdout.split("\n") |
| for line in lines: |
| if line == "" or line[0] == "#": |
| continue |
| ldefault = re.findall(r"{}=.+".format(grubcmdline), line) |
| if ldefault: |
| self._default_cmdline = ldefault[0] |
| break |
| |
| def get_current_cmdline(self): |
| """ |
| Returns the saved grub cmdline |
| |
| :returns: The saved grub cmdline |
| :rtype: string |
| """ |
| return self._current_cmdline |
| |
| def get_default_cmdline(self): |
| """ |
| Returns the default grub cmdline |
| |
| :returns: The default grub cmdline |
| :rtype: string |
| """ |
| return self._default_cmdline |
| |
| def create_cmdline(self, isolated_cpus): |
| """ |
| Create the new grub cmdline |
| |
| :param isolated_cpus: The isolated cpu string |
| :type isolated_cpus: string |
| :returns: The command line |
| :rtype: string |
| """ |
| grubcmdline = self._node["cpu"]["grubcmdline"] |
| cmdline = self._default_cmdline |
| value = cmdline.split("{}=".format(grubcmdline))[1] |
| value = value.rstrip('"').lstrip('"') |
| |
| # jadfix intel_pstate=disable sometimes cause networks to |
| # hang on reboot |
| # iommu = re.findall(r'iommu=\w+', value) |
| # pstate = re.findall(r'intel_pstate=\w+', value) |
| # If there is already some iommu commands set, leave them, |
| # if not use ours |
| # if iommu == [] and pstate == []: |
| # value = '{} intel_pstate=disable'.format(value) |
| |
| # Replace isolcpus with ours |
| isolcpus = re.findall(r"isolcpus=[\w+\-,]+", value) |
| if not isolcpus: |
| if isolated_cpus != "": |
| value = "{} isolcpus={}".format(value, isolated_cpus) |
| else: |
| if isolated_cpus != "": |
| value = re.sub( |
| r"isolcpus=[\w+\-,]+", "isolcpus={}".format(isolated_cpus), value |
| ) |
| else: |
| value = re.sub(r"isolcpus=[\w+\-,]+", "", value) |
| |
| nohz = re.findall(r"nohz_full=[\w+\-,]+", value) |
| if not nohz: |
| if isolated_cpus != "": |
| value = "{} nohz_full={}".format(value, isolated_cpus) |
| else: |
| if isolated_cpus != "": |
| value = re.sub( |
| r"nohz_full=[\w+\-,]+", "nohz_full={}".format(isolated_cpus), value |
| ) |
| else: |
| value = re.sub(r"nohz_full=[\w+\-,]+", "", value) |
| |
| rcu = re.findall(r"rcu_nocbs=[\w+\-,]+", value) |
| if not rcu: |
| if isolated_cpus != "": |
| value = "{} rcu_nocbs={}".format(value, isolated_cpus) |
| else: |
| if isolated_cpus != "": |
| value = re.sub( |
| r"rcu_nocbs=[\w+\-,]+", "rcu_nocbs={}".format(isolated_cpus), value |
| ) |
| else: |
| value = re.sub(r"rcu_nocbs=[\w+\-,]+", "", value) |
| |
| value = value.lstrip(" ").rstrip(" ") |
| cmdline = '{}="{}"'.format(grubcmdline, value) |
| return cmdline |
| |
| def apply_cmdline(self, node, isolated_cpus): |
| """ |
| Apply cmdline to the default grub file |
| |
| :param node: Node dictionary with cpuinfo. |
| :param isolated_cpus: The isolated cpu string |
| :type node: dict |
| :type isolated_cpus: string |
| :return The vpp cmdline |
| :rtype string |
| """ |
| |
| vpp_cmdline = self.create_cmdline(isolated_cpus) |
| if len(vpp_cmdline): |
| # Update grub |
| # Save the original file |
| rootdir = node["rootdir"] |
| grubcmdline = node["cpu"]["grubcmdline"] |
| ofilename = rootdir + node["cpu"]["grub_config_file"] + ".orig" |
| filename = rootdir + node["cpu"]["grub_config_file"] |
| |
| # Write the output file |
| # Does a copy of the original file exist, if not create one |
| (ret, stdout, stderr) = VPPUtil.exec_command("ls {}".format(ofilename)) |
| if ret != 0: |
| if stdout.strip("\n") != ofilename: |
| cmd = "sudo cp {} {}".format(filename, ofilename) |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError( |
| "{} failed on node {} {}".format( |
| cmd, self._node["host"], stderr |
| ) |
| ) |
| |
| # Get the contents of the current grub config file |
| cmd = "cat {}".format(filename) |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError( |
| "{} failed on node {} {}".format(cmd, self._node["host"], stderr) |
| ) |
| |
| # Write the new contents |
| # Get the Default Linux command line, ignoring commented lines |
| content = "" |
| lines = stdout.split("\n") |
| for line in lines: |
| if line == "": |
| content += line + "\n" |
| continue |
| if line[0] == "#": |
| content += line + "\n" |
| continue |
| |
| ldefault = re.findall(r"{}=.+".format(grubcmdline), line) |
| if ldefault: |
| content += vpp_cmdline + "\n" |
| else: |
| content += line + "\n" |
| |
| content = content.replace(r"`", r"\`") |
| content = content.rstrip("\n") |
| cmd = "sudo cat > {0} << EOF\n{1}\n".format(filename, content) |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError( |
| "{} failed on node {} {}".format(cmd, self._node["host"], stderr) |
| ) |
| |
| return vpp_cmdline |
| |
| def __init__(self, node): |
| distro = VPPUtil.get_linux_distro() |
| if distro[0] == "Ubuntu": |
| node["cpu"]["grubcmdline"] = "GRUB_CMDLINE_LINUX_DEFAULT" |
| else: |
| node["cpu"]["grubcmdline"] = "GRUB_CMDLINE_LINUX" |
| |
| self._node = node |
| self._current_cmdline = "" |
| self._default_cmdline = "" |
| self._get_current_cmdline() |
| self._get_default_cmdline() |