Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ubuntu/devel #5527

Merged
merged 76 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
eb388c7
ci: PR update checklist GH- anchors to align w/ later template (#5449)
blackboxsw Jun 27, 2024
8470af0
tests: hard-code curtin-dev ppa instead of canonical-kernel-team (#5450)
blackboxsw Jun 27, 2024
5250260
Support metalink in yum repository config (#5444)
ani-sinha Jun 27, 2024
2c09f69
fix(net): klibc ipconfig PROTO compatibility (#5437)
alexsander-souza Jun 27, 2024
790d229
fix: Add get_connection_with_tls_context() for requests 2.32.2+ (#5435)
eaglegai Jun 27, 2024
6ee0079
chore(cmdline): Update comments (#5458)
holmanb Jun 27, 2024
3851c5c
test: Add ds-identify integration test coverage (#5394)
holmanb Jun 27, 2024
12f1198
fix(openstack): Fix bond mac_address (#5369)
jcmoore3 Jun 4, 2024
f8f9d19
test(openstack): Test bond mac address (#5369)
holmanb Jun 27, 2024
bcc5920
fix: Gracefully handle missing files (#5397)
jcmoore3 Jun 27, 2024
e80514b
feat: Add deprecation boundary to logger (#5411)
holmanb Jun 18, 2024
8906e17
feat: Add deprecation boundary support to schema validator (#5411)
holmanb Jun 19, 2024
7f98af9
test: Add unit tests for features.DEPRECATION_INFO_BOUNDARY (#5411)
blackboxsw Jun 27, 2024
bbcc67d
doc(refactor): Convert module docs to new system (#5427)
s-makin Jun 27, 2024
70f7e78
fix: Do not add the vlan_mac_address field into the VLAN object (#5365)
jcmoore3 Jun 27, 2024
b3618d4
fix(schema): permit deprecated hyphenated keys under users key (#5456)
blackboxsw Jun 28, 2024
371b236
fix: Ensure properties for bonded interfaces are properly translated …
jcmoore3 Jun 4, 2024
debafbc
test: fix unit test openstack vlan mac_address (#5367)
aciba90 Jun 28, 2024
121539b
fix(schema): Don't report changed keys as deprecated (#5464)
holmanb Jun 28, 2024
726159b
fix(test): Mock version boundary (#5464)
holmanb Jun 28, 2024
25669f7
fix: dont double-log deprecated INFOs (#5465)
TheRealFalcon Jun 28, 2024
5ce2ee3
chore: fix schema.py formatting (#5465)
TheRealFalcon Jun 28, 2024
0a698a5
test: Fix deprecation test failures (#5466)
TheRealFalcon Jun 28, 2024
7693ee2
tests: update keyserver PPA key fur curtin-dev (#5472)
blackboxsw Jul 1, 2024
d59724a
tests: integration tests aware of features.DEPRECATION_INFO_BOUNDARY
blackboxsw Jul 1, 2024
681b7de
fix(rh_subscription): add string type to org (#5453)
aciba90 Jul 1, 2024
ee1b25b
tests: update nocloud deprecation test for boundary version (#5474)
blackboxsw Jul 1, 2024
c45280e
tests: update ubuntu_pro test to account for info-level deprecations …
blackboxsw Jul 1, 2024
40e2eb4
test: Update integration tests to pass on focal (#5476)
TheRealFalcon Jul 1, 2024
41c375c
ci(mypy): Set default follow_imports value (#5350)
holmanb May 31, 2024
16018b2
fix(typing): Remove invalid type annotations (#5350)
holmanb Jun 1, 2024
5550285
fix(typing): Remove type annotation for unused variable (#5350)
holmanb Jun 1, 2024
a8cf24b
fix(typing): Add / update type annotations (#5350)
holmanb Jun 1, 2024
b26d388
refactor(typing): Remove unused code paths (#5350)
holmanb Jun 1, 2024
97146b5
chore(typing): Remove type ignores and casts (#5350)
holmanb Jun 1, 2024
e4c05e8
fix(tox): Update tox.ini (#5350)
holmanb Jun 3, 2024
73a5c51
chore: Auto-format network jsonschema in ci (#5350)
holmanb Jun 3, 2024
8ad0bba
type: Add stub types for network v1/v2 config (#5350)
holmanb Jun 10, 2024
c95ec1a
chore(mypy): Drop unused missing import exclusions (#5350)
holmanb Jun 21, 2024
2bca69a
fix(test): Fix ip printing for non-lxd instances (#5350)
holmanb Jul 1, 2024
291aabe
fix(test): Fix pycloudlib types in integration tests (#5350)
holmanb Jul 1, 2024
b0d6c7d
test: Add jsonschema guard in test_cc_ubuntu_pro.py (#5479)
TheRealFalcon Jul 2, 2024
0a7036e
fix: correct deprecated_version=22.2 for users.sudo
blackboxsw Jul 2, 2024
0a4c43d
test: Fix no default user in test_status.py (#5478)
TheRealFalcon Jul 2, 2024
fd63297
Release 24.2 (#5481)
TheRealFalcon Jul 3, 2024
9357c38
feat(aosc): Add 'AOSC OS' support (#5310)
leavelet Jul 3, 2024
053331e
fix(openbsd): fix mtu on newline in hostname files (#5412)
tobias-urdin Jul 3, 2024
2b6fe64
fix(vmware): Set IPv6 to dhcp when there is no IPv6 addr (#5471)
PengpengSun Jul 3, 2024
0af459e
test: pytestify and cleanup test_cc_mounts.py (#5459)
TheRealFalcon Jul 8, 2024
8a58270
test: Ensure mkcert executable in ftp tests (#5493)
TheRealFalcon Jul 9, 2024
7130bbb
test: Add missing assert to test_status.py (#5494)
TheRealFalcon Jul 9, 2024
db828d0
typing: fix check_untyped_defs in cloudinit.util (#5490)
aciba90 Jul 5, 2024
188656b
refactor: util.get_proc_env to work with strs (#5490)
aciba90 Jul 9, 2024
0128716
refactor: util.mounts to handle errors (#5490)
aciba90 Jul 9, 2024
4c0468c
Set MTU for bond parent interface (#5495)
jcmoore3 Jul 10, 2024
7d35664
fix: add schema rules for 'baseurl' and 'metalink' in yum repo config…
ani-sinha Jul 10, 2024
4abdd5a
feat(systemd): Warn user of unexpected run mode (#5209)
holmanb Apr 24, 2024
604d80e
test: Don't fail tests which call cloud-init as a command (#5209)
holmanb Jun 4, 2024
8aa1c30
test: allow verify_clean_boot to ignore all or specific tracebacks (#…
blackboxsw Jun 19, 2024
75add5c
feat(systemd): convert warning level message to deprecation (#5209)
blackboxsw Jun 19, 2024
a911d07
fix(test): Fix ip printer for non-lxd (#5488)
holmanb Jul 10, 2024
18d76ac
tests: revert expectation of exit 2 from cloud-init init --local (#5504)
blackboxsw Jul 10, 2024
8dbc5c2
test: Unconditionally skip test_multi_nic_hotplug_vpc (#5503)
TheRealFalcon Jul 10, 2024
e0e6a42
Fix configuration of DNS servers via OpenStack (#5384)
jcmoore3 Jul 11, 2024
311f723
fix: Update DNS behavior for NetworkManager interfaces (#5496)
jcmoore3 Jul 18, 2024
658d184
doc(OFV): Document how to configure cloud-init (#5519)
holmanb Jul 18, 2024
0b40843
Support setting mirrorlist in yum repository config (#5522)
ani-sinha Jul 18, 2024
550c685
fix: Clean cache if no datasource fallback (#5499)
TheRealFalcon Jul 18, 2024
57d130e
chore(formatting): fix squashed commit test formatting (#5524)
blackboxsw Jul 18, 2024
b0a673a
feat: Add trace-level logger (#5414)
TheRealFalcon Jun 19, 2024
8ec2f64
refactor: replace verbosity with log levels in logs.py (#5414)
TheRealFalcon Jun 19, 2024
19c86ff
refactor: logs.py pathlib changes (#5414)
TheRealFalcon Jun 19, 2024
6e4153b
refactor: logs.py add typing and small misc refactors (#5414)
TheRealFalcon Jun 19, 2024
08767fd
merge from upstream/main at 24.2-28-g6e4153b3
aciba90 Jul 19, 2024
669f8f4
update changelog (new upstream snapshot)
aciba90 Jul 19, 2024
87c915f
releasing cloud-init version 24.3~1g6e4153b3-0ubuntu1
aciba90 Jul 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ To ease the process of reviewing your PR, do make sure to complete the following
- Test files should map to source files i.e. a source file ``cloudinit/example.py`` should be tested by ``tests/unittests/test_example.py``
- Run unit tests with ``tox -e py3``
- [ ] I have kept the change small, avoiding unnecessary whitespace or non-functional changes.
- [ ] I have added a reference to issues that this PR relates to in the PR message (Refs #1234, Closes #1234)
- [ ] I have added a reference to issues that this PR relates to in the PR message (Refs GH-1234, Fixes GH-1234)
- [ ] I have updated the documentation with the changed behavior.
- If the change doesn't change the user interface and is trivial, this step may be skipped.
- Cloud-config documentation is generated from the jsonschema.
Expand Down
364 changes: 364 additions & 0 deletions ChangeLog

Large diffs are not rendered by default.

171 changes: 87 additions & 84 deletions cloudinit/cmd/devel/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
"""Define 'collect-logs' utility and handler to include in cloud-init cmd."""

import argparse
import logging
import os
import pathlib
import shutil
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import NamedTuple, Optional
from typing import List, NamedTuple, Optional, cast

from cloudinit import log
from cloudinit.cmd.devel import read_cfg_paths
from cloudinit.stages import Init
from cloudinit.subp import ProcessExecutionError, subp
Expand All @@ -27,6 +29,8 @@
write_file,
)

LOG = cast(log.CustomLoggerType, logging.getLogger(__name__))


class LogPaths(NamedTuple):
userdata_raw: str
Expand Down Expand Up @@ -96,7 +100,9 @@ class ApportFile(NamedTuple):
]


def get_parser(parser=None):
def get_parser(
parser: Optional[argparse.ArgumentParser] = None,
) -> argparse.ArgumentParser:
"""Build or extend and arg parser for collect-logs utility.

@param parser: Optional existing ArgumentParser instance representing the
Expand Down Expand Up @@ -141,7 +147,7 @@ def get_parser(parser=None):
return parser


def _get_copytree_ignore_files(paths: LogPaths):
def _get_copytree_ignore_files(paths: LogPaths) -> List[str]:
"""Return a list of files to ignore for /run/cloud-init directory"""
ignored_files = [
"hook-hotplug-cmd", # named pipe for hotplug
Expand All @@ -152,111 +158,109 @@ def _get_copytree_ignore_files(paths: LogPaths):
return ignored_files


def _write_command_output_to_file(cmd, filename, msg, verbosity):
def _write_command_output_to_file(
cmd: List[str],
file_path: pathlib.Path,
msg: str,
) -> Optional[str]:
"""Helper which runs a command and writes output or error to filename."""
ensure_dir(os.path.dirname(filename))
file_path.parent.mkdir(parents=True, exist_ok=True)
try:
output = subp(cmd).stdout
except ProcessExecutionError as e:
write_file(filename, str(e))
_debug("collecting %s failed.\n" % msg, 1, verbosity)
write_file(file_path, str(e))
LOG.debug("collecting %s failed.", msg)
output = None
else:
write_file(filename, output)
_debug("collected %s\n" % msg, 1, verbosity)
return output
write_file(file_path, output)
LOG.debug("collected %s to file '%s'", msg, file_path.stem)
return output


def _stream_command_output_to_file(cmd, filename, msg, verbosity):
"""Helper which runs a command and writes output or error to filename."""
ensure_dir(os.path.dirname(filename))
def _stream_command_output_to_file(
cmd: List[str], file_path: pathlib.Path, msg: str
) -> None:
"""Helper which runs a command and writes output or error to filename.

`subprocess.call` is invoked directly here to stream output to the file.
Otherwise memory usage can be high for large outputs.
"""
file_path.parent.mkdir(parents=True, exist_ok=True)
try:
with open(filename, "w") as f:
with file_path.open("w") as f:
subprocess.call(cmd, stdout=f, stderr=f) # nosec B603
except OSError as e:
write_file(filename, str(e))
_debug("collecting %s failed.\n" % msg, 1, verbosity)
write_file(file_path, str(e))
LOG.debug("collecting %s failed.", msg)
else:
_debug("collected %s\n" % msg, 1, verbosity)
LOG.debug("collected %s to file '%s'", msg, file_path.stem)


def _debug(msg, level, verbosity):
if level <= verbosity:
sys.stderr.write(msg)


def _collect_file(path, out_dir, verbosity):
def _collect_file(path: str, out_dir: str) -> None:
if os.path.isfile(path):
copy(path, out_dir)
_debug("collected file: %s\n" % path, 1, verbosity)
LOG.debug("collected file: %s", path)
else:
_debug("file %s did not exist\n" % path, 2, verbosity)
LOG.trace("file %s did not exist", path)


def _collect_installer_logs(
log_dir: str, include_userdata: bool, verbosity: int
):
def _collect_installer_logs(log_dir: str, include_userdata: bool) -> None:
"""Obtain subiquity logs and config files."""
for src_file in INSTALLER_APPORT_FILES:
destination_dir = Path(log_dir + src_file.path).parent
destination_dir = pathlib.Path(log_dir + src_file.path).parent
if not destination_dir.exists():
ensure_dir(str(destination_dir))
_collect_file(src_file.path, str(destination_dir), verbosity)
_collect_file(src_file.path, str(destination_dir))
if include_userdata:
for src_file in INSTALLER_APPORT_SENSITIVE_FILES:
destination_dir = Path(log_dir + src_file.path).parent
destination_dir = pathlib.Path(log_dir + src_file.path).parent
if not destination_dir.exists():
ensure_dir(str(destination_dir))
_collect_file(src_file.path, str(destination_dir), verbosity)
_collect_file(src_file.path, str(destination_dir))


def _collect_version_info(log_dir: str, verbosity: int):
def _collect_version_info(log_dir: str) -> None:
version = _write_command_output_to_file(
cmd=["cloud-init", "--version"],
filename=os.path.join(log_dir, "version"),
file_path=pathlib.Path(log_dir, "version"),
msg="cloud-init --version",
verbosity=verbosity,
)
dpkg_ver = _write_command_output_to_file(
cmd=["dpkg-query", "--show", "-f=${Version}\n", "cloud-init"],
filename=os.path.join(log_dir, "dpkg-version"),
file_path=pathlib.Path(log_dir, "dpkg-version"),
msg="dpkg version",
verbosity=verbosity,
)
if not version:
version = dpkg_ver if dpkg_ver else "not-available"
_debug("collected cloud-init version: %s\n" % version, 1, verbosity)
version = dpkg_ver or "not-available"


def _collect_system_logs(log_dir: str, verbosity: int):
def _collect_system_logs(log_dir: str) -> None:
_stream_command_output_to_file(
cmd=["dmesg"],
filename=os.path.join(log_dir, "dmesg.txt"),
file_path=pathlib.Path(log_dir, "dmesg.txt"),
msg="dmesg output",
verbosity=verbosity,
)
_stream_command_output_to_file(
cmd=["journalctl", "--boot=0", "-o", "short-precise"],
filename=os.path.join(log_dir, "journal.txt"),
file_path=pathlib.Path(log_dir, "journal.txt"),
msg="systemd journal of current boot",
verbosity=verbosity,
)


def _collect_cloudinit_logs(
log_dir: str,
verbosity: int,
init: Init,
paths: LogPaths,
include_userdata: bool,
):
for log in get_config_logfiles(init.cfg):
_collect_file(log, log_dir, verbosity)
) -> None:
for logfile in get_config_logfiles(init.cfg):
_collect_file(logfile, log_dir)
if include_userdata:
user_data_file = paths.userdata_raw
_collect_file(user_data_file, log_dir, verbosity)
_collect_file(user_data_file, log_dir)


def _collect_run_dir(log_dir: str, verbosity: int, paths: LogPaths):
def _collect_run_dir(log_dir: str, paths: LogPaths) -> None:
run_dir = os.path.join(log_dir, "run")
ensure_dir(run_dir)
if os.path.exists(paths.run_dir):
Expand All @@ -267,38 +271,31 @@ def _collect_run_dir(log_dir: str, verbosity: int, paths: LogPaths):
ignore=lambda _, __: _get_copytree_ignore_files(paths),
)
except shutil.Error as e:
sys.stderr.write("Failed collecting file(s) due to error:\n")
sys.stderr.write(str(e) + "\n")
_debug("collected dir %s\n" % paths.run_dir, 1, verbosity)
LOG.warning("Failed collecting file(s) due to error: %s", e)
LOG.debug("collected directory: %s", paths.run_dir)
else:
_debug(
"directory '%s' did not exist\n" % paths.run_dir,
1,
verbosity,
)
LOG.debug("directory '%s' did not exist", paths.run_dir)
if os.path.exists(os.path.join(paths.run_dir, "disabled")):
# Fallback to grab previous cloud/data
cloud_data_dir = Path(paths.cloud_data)
cloud_data_dir = pathlib.Path(paths.cloud_data)
if cloud_data_dir.exists():
shutil.copytree(
str(cloud_data_dir),
Path(log_dir + str(cloud_data_dir)),
pathlib.Path(log_dir + str(cloud_data_dir)),
)


def collect_logs(
tarfile: str, include_userdata: bool, verbosity: int = 0
) -> int:
def collect_logs(tarfile: str, include_userdata: bool) -> int:
"""Collect all cloud-init logs and tar them up into the provided tarfile.

@param tarfile: The path of the tar-gzipped file to create.
@param include_userdata: Boolean, true means include user-data.
@return: 0 on success, 1 on failure.
"""
if include_userdata and os.getuid() != 0:
sys.stderr.write(
"To include userdata, root user is required."
" Try sudo cloud-init collect-logs\n"
LOG.error(
"To include userdata, root user is required. "
"Try sudo cloud-init collect-logs"
)
return 1

Expand All @@ -312,33 +309,39 @@ def collect_logs(
init.read_cfg()
paths = get_log_paths(init)

_collect_version_info(log_dir, verbosity)
_collect_system_logs(log_dir, verbosity)
_collect_cloudinit_logs(
log_dir, verbosity, init, paths, include_userdata
)
_collect_installer_logs(log_dir, include_userdata, verbosity)
_collect_run_dir(log_dir, verbosity, paths)
_collect_version_info(log_dir)
_collect_system_logs(log_dir)
_collect_cloudinit_logs(log_dir, init, paths, include_userdata)
_collect_installer_logs(log_dir, include_userdata)
_collect_run_dir(log_dir, paths)
with chdir(tmp_dir):
subp(["tar", "czvf", tarfile, log_dir.replace(f"{tmp_dir}/", "")])
sys.stderr.write("Wrote %s\n" % tarfile)
LOG.info("Wrote %s", tarfile)
return 0


def handle_collect_logs_args(name, args):
def _setup_logger(verbosity: int) -> None:
log.reset_logging()
if verbosity == 0:
level = logging.INFO
elif verbosity == 1:
level = logging.DEBUG
else:
level = log.TRACE
LOG.setLevel(level)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(message)s"))
LOG.addHandler(handler)


def handle_collect_logs_args(_name: str, args: argparse.Namespace) -> int:
"""Handle calls to 'cloud-init collect-logs' as a subcommand."""
_setup_logger(args.verbosity)
return collect_logs(
tarfile=args.tarfile,
include_userdata=args.userdata,
verbosity=args.verbosity,
)


def main():
"""Tool to collect and tar all cloud-init related logs."""
parser = get_parser()
return handle_collect_logs_args("collect-logs", parser.parse_args())


if __name__ == "__main__":
sys.exit(main())
sys.exit(handle_collect_logs_args("", get_parser().parse_args()))
Loading
Loading