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.key}={self.value.value}"
return self.key.key
[docs]
@dataclass(frozen=True)
class KernelCommandLine:
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)
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
# 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)