# 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()