Source code for ironic.common.kernel_parameters
# 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.
import lark
from dataclasses import dataclass
from ironic.common.exception import InvalidParameterValue
from ironic.common.i18n import _
[docs]
def sanitize_kernel_command_line(command_line: str) -> str:
"""Applies filtering to a command line to sanitize it.
NOTE: This does not guarantee a correct or safe kernel command line,
for stronger guarantees of correctness and safety use
KernelCommandLine.parse().
:param command_line: A string containing a kernel command line or
individual parameters.
:returns: A filtered string which should be safer for use.
"""
return ''.join(c for c in command_line if c not in {'\n', '\r', '\0'})
[docs]
@dataclass(frozen=True)
class ParameterKey:
key: str
def __str__(self):
return self.key
[docs]
@dataclass(frozen=True)
class ParameterValue:
value: str
def __str__(self):
if ' ' in self.value:
return f"\"{self.value}\""
return self.value
[docs]
@dataclass(frozen=True)
class KernelParameter:
key: ParameterKey
value: ParameterValue
def __str__(self):
if len(self.value.value) > 0:
return f"{self.key}={self.value}"
return self.key.key
[docs]
@dataclass(frozen=True)
class KernelCommandLine:
"""Represents a kernel command line.
NOTE: An instantiated object of this type does not guarantee that it has
passed parsing via KernelParameterParser. ParsedKernelCommandLine is
reserved for this distinction.
"""
parameters: dict[str, list[KernelParameter]]
init_args: str
def __str__(self):
output = ' '.join(
' '.join(str(param) for param in param_list)
for param_list in self.parameters.values())
if len(self.init_args) > 0:
output += " -- " + self.init_args
return output
[docs]
@classmethod
def parse(cls, command_line: str):
try:
tree = KernelParameterParser.parse(command_line.strip())
return KernelParameterTransformer().transform(tree)
except (lark.exceptions.LarkError,
lark.exceptions.UnexpectedInput) as e:
raise InvalidParameterValue(
_('Kernel command line did not parse: "%s" -- %s') \
% (command_line, str(e))) from None
[docs]
@dataclass(frozen=True)
class ParsedKernelCommandLine(KernelCommandLine):
"""Represents a parsed kernel command line.
NOTE: This object should only be instantiated via passing a kernel
command line string through KernelParameterParser.parse() and then
the resultant parse tree through KernelParameterTransformer.transform().
This helps distinguish between kernel command lines that have been
validated by parsing and those that have not.
"""
pass
# NOTE(clif): Some valid values (such as filenames) are not going to be
# representable given we're explicitly not allowing large swaths characters
# in parameter values. I believe this is reasonable. Most people do not
# purposefully put non-printable/control characters in kernel parameters for
# anything other than nefarious goals.
# NOTE(clif): bare_value and value_with_spaces permit a *large* range of
# printable ASCII characters because many are used as characters with special
# meaning in several kernel parameters.
# NOTE(clif): The only permitted white-space character in this grammar is a
# space.
KERNEL_PARAMETER_GRAMMAR = r"""
kernel_command_line: parameter_list [init_suffix]
parameter_list: parameter*(" "+ parameter)*
parameter: key
| key_value_pair
key_value_pair: key"="value
key: /[A-Za-z0-9_\-\.]+/
value: bare_value
| quoted_value
quoted_value: "\"" value_with_spaces "\""
bare_value: /[\!\#-\\.0-9:-\@A-Z\[-~]+/
value_with_spaces: /[\!\#-\\.0-9:-\@A-Z\[-~ ]+/
init_suffix: " "+ "--" " "+ init_arguments
init_arguments: value_with_spaces
"""
KERNEL_PARAMETER_GRAMMER_START_RULE = 'kernel_command_line'
KernelParameterParser = lark.Lark(KERNEL_PARAMETER_GRAMMAR,
start=KERNEL_PARAMETER_GRAMMER_START_RULE,
strict=True)