import OL WALinuxAgent-2.13.1.1-2.0.1.el10_1.1
This commit is contained in:
parent
d82710401b
commit
c9f19a0bca
26
.gitignore
vendored
26
.gitignore
vendored
@ -1,25 +1 @@
|
||||
/WALinuxAgent-b3f2619a854455675ae5f2ee14726659e0398af7.tar.gz
|
||||
/WALinuxAgent-26785b64279913d416767a6288a3b3f970ed4522.tar.gz
|
||||
/WALinuxAgent-30019ae2c10a5c78f55d4ec342db07366abcc602.tar.gz
|
||||
/WALinuxAgent-54d4ebde1a626e6bd000abdfd518e150b0495f37.tar.gz
|
||||
/WALinuxAgent-5bb9df7f2f485dbd5be057bbb657afe54bc1bf1b.tar.gz
|
||||
/WALinuxAgent-2.2.25.tar.gz
|
||||
/WALinuxAgent-2.2.32.tar.gz
|
||||
/WALinuxAgent-2.2.37.tar.gz
|
||||
/WALinuxAgent-2.2.38.tar.gz
|
||||
/WALinuxAgent-2.2.40.tar.gz
|
||||
/WALinuxAgent-2.2.46.tar.gz
|
||||
/v2.2.48.1.tar.gz
|
||||
/WALinuxAgent-2.2.52.tar.gz
|
||||
/v2.2.52.tar.gz
|
||||
/module-setup.sh
|
||||
/v2.2.54.2.tar.gz
|
||||
/v2.3.0.2.tar.gz
|
||||
/v2.3.1.1.tar.gz
|
||||
/v2.5.0.2.tar.gz
|
||||
/v2.7.0.6.tar.gz
|
||||
/v2.7.1.0.tar.gz
|
||||
/v2.7.3.0.tar.gz
|
||||
/v2.8.0.11.tar.gz
|
||||
/v2.9.0.4.tar.gz
|
||||
/v2.9.1.1.tar.gz
|
||||
v2.13.1.1.tar.gz
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
From dac5101c56b59dbb14d96d4344d6cb2ac047b392 Mon Sep 17 00:00:00 2001
|
||||
From 2d78c9ebaba4742390e92dc5994391949b90ec4c Mon Sep 17 00:00:00 2001
|
||||
From: Chris Patterson <cpatterson@microsoft.com>
|
||||
Date: Thu, 1 Sep 2022 10:45:47 -0400
|
||||
Subject: [PATCH] waagent.service: set ConditionVirtualization=|microsoft
|
||||
@ -10,10 +10,10 @@ test setups to add another condition (i.e. run outside of hyperv).
|
||||
|
||||
Signed-off-by: Chris Patterson <cpatterson@microsoft.com>
|
||||
---
|
||||
bin/waagent2.0 | 1 +
|
||||
init/redhat/py2/waagent.service | 1 +
|
||||
init/redhat/waagent.service | 1 +
|
||||
init/waagent.service | 1 +
|
||||
bin/waagent2.0 | 1 +
|
||||
init/redhat/py2/waagent.service | 1 +
|
||||
init/redhat/waagent.service | 1 +
|
||||
init/waagent.service | 1 +
|
||||
4 files changed, 4 insertions(+)
|
||||
|
||||
diff --git a/bin/waagent2.0 b/bin/waagent2.0
|
||||
@ -29,7 +29,7 @@ index 34732677..c84c8c40 100644
|
||||
[Service]
|
||||
Type=simple
|
||||
diff --git a/init/redhat/py2/waagent.service b/init/redhat/py2/waagent.service
|
||||
index c6d15420..132e7027 100644
|
||||
index 920e0ec7..46254ec3 100644
|
||||
--- a/init/redhat/py2/waagent.service
|
||||
+++ b/init/redhat/py2/waagent.service
|
||||
@@ -5,6 +5,7 @@ After=network-online.target
|
||||
@ -41,7 +41,7 @@ index c6d15420..132e7027 100644
|
||||
[Service]
|
||||
Type=simple
|
||||
diff --git a/init/redhat/waagent.service b/init/redhat/waagent.service
|
||||
index dc11fbb1..7c93b101 100644
|
||||
index 2c6ac5d8..12d5feee 100644
|
||||
--- a/init/redhat/waagent.service
|
||||
+++ b/init/redhat/waagent.service
|
||||
@@ -5,6 +5,7 @@ After=network-online.target
|
||||
@ -64,6 +64,3 @@ index e91f1433..aa1f3203 100644
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
--
|
||||
2.37.3
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
From 896d0f1edfbfad20c2eecb04fca17e7dc335dfb8 Mon Sep 17 00:00:00 2001
|
||||
From cb221e0885f794e0ec302cbb77bff927a8d4458a Mon Sep 17 00:00:00 2001
|
||||
From: Ani Sinha <anisinha@redhat.com>
|
||||
Date: Mon, 6 May 2024 11:50:49 +0530
|
||||
Subject: [PATCH] Disable automatic log collector
|
||||
@ -16,12 +16,16 @@ Jira: https://issues.redhat.com/browse/RHEL-35963
|
||||
Upstream: RHEL only.
|
||||
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
|
||||
Patch-name: wla-Disable-automatic-log-collector.patch
|
||||
Patch-id:
|
||||
Patch-present-in-specfile: True
|
||||
---
|
||||
config/waagent.conf | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/config/waagent.conf b/config/waagent.conf
|
||||
index 7316dc2d..4a345fc0 100644
|
||||
index 3c9ad5d4..62d8148e 100644
|
||||
--- a/config/waagent.conf
|
||||
+++ b/config/waagent.conf
|
||||
@@ -70,7 +70,7 @@ Logs.Verbose=n
|
||||
@ -33,6 +37,3 @@ index 7316dc2d..4a345fc0 100644
|
||||
|
||||
# How frequently to collect logs, default is each hour
|
||||
Logs.CollectPeriod=3600
|
||||
--
|
||||
2.39.3
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
From 8cc6f62bd1be39e60be6ae606ea4beb76ae24d7c Mon Sep 17 00:00:00 2001
|
||||
From b0c1a1641973b0444045a4906d80e0b16ff755e7 Mon Sep 17 00:00:00 2001
|
||||
From: Mohammed Gamal <mgamal@redhat.com>
|
||||
Date: Fri, 29 Jul 2022 13:07:13 +0200
|
||||
Subject: [PATCH 1/4] redhat: Use NetworkManager to set DHCP hostnames on
|
||||
recent RHEL distros
|
||||
Subject: [PATCH] redhat: Use NetworkManager to set DHCP hostnames on recent
|
||||
RHEL distros
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 13: sync c10s branch from c9s
|
||||
@ -32,18 +32,22 @@ Patch-name: wla-redhat-Use-NetworkManager-to-set-DHCP-hostnames-on-r.patch
|
||||
Patch-id:
|
||||
Patch-present-in-specfile: True
|
||||
(cherry picked from commit 8400a993c6c27f8f8fc598f81e2c329dc8255805)
|
||||
|
||||
Patch-name: wla-redhat-Use-NetworkManager-to-set-DHCP-hostnames-on-r.patch
|
||||
Patch-id:
|
||||
Patch-present-in-specfile: True
|
||||
---
|
||||
azurelinuxagent/common/osutil/redhat.py | 12 ++++++++++++
|
||||
1 file changed, 12 insertions(+)
|
||||
|
||||
diff --git a/azurelinuxagent/common/osutil/redhat.py b/azurelinuxagent/common/osutil/redhat.py
|
||||
index 312dd160..4b46a97a 100644
|
||||
index a9a10347..b85b2d42 100644
|
||||
--- a/azurelinuxagent/common/osutil/redhat.py
|
||||
+++ b/azurelinuxagent/common/osutil/redhat.py
|
||||
@@ -164,3 +164,15 @@ class RedhatOSModernUtil(RedhatOSUtil):
|
||||
time.sleep(wait)
|
||||
else:
|
||||
logger.warn("exceeded restart retries")
|
||||
@@ -272,3 +272,15 @@ class RedhatOSModernUtil(RedhatOSUtil):
|
||||
# NetworkManager restart in RedhatOSModernUtil because the issue was not reproduced on these versions.
|
||||
shellutil.run("service NetworkManager restart")
|
||||
DefaultOSUtil.publish_hostname(self, hostname)
|
||||
+
|
||||
+ def set_dhcp_hostname(self, hostname):
|
||||
+ """
|
||||
@ -56,6 +60,3 @@ index 312dd160..4b46a97a 100644
|
||||
+
|
||||
+ if return_code != 0:
|
||||
+ logger.error("failed to set DHCP hostname for interface {0}: return code {1}".format(ifname, return_code))
|
||||
--
|
||||
2.39.3
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
From dd3a8f78ef32aed2f1267dd7dd495c65edd259a1 Mon Sep 17 00:00:00 2001
|
||||
From c17811fbdb131a4cca41a847e8b666f432dbe4a8 Mon Sep 17 00:00:00 2001
|
||||
From: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
Date: Mon, 6 Jan 2025 17:13:11 +0100
|
||||
Subject: [PATCH] redhat: Add a udev rule to avoid managing slave NICs with
|
||||
@ -18,6 +18,10 @@ eventually. For the time being, just put the required udev rule into
|
||||
WALinuxAgent package.
|
||||
|
||||
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
|
||||
Patch-name: wla-redhat-Add-a-udev-rule-to-avoid-managing-slave-NICs-.patch
|
||||
Patch-id:
|
||||
Patch-present-in-specfile: True
|
||||
---
|
||||
config/10-azure-unmanaged-sriov.rules | 6 ++++++
|
||||
setup.py | 3 ++-
|
||||
@ -37,7 +41,7 @@ index 00000000..7eefcb26
|
||||
+# NM_UNMANAGED=1 for NetworkManager
|
||||
+SUBSYSTEM=="net", ACTION!="remove", DRIVERS=="mana|mlx4_core|mlx5_core", ATTR{flags}=="0x?[89ABCDEF]??", ENV{AZURE_UNMANAGED_SRIOV}="1", ENV{ID_NET_MANAGED_BY}="unmanaged", ENV{NM_UNMANAGED}="1"
|
||||
diff --git a/setup.py b/setup.py
|
||||
index 8f5d92b4..f76aff30 100755
|
||||
index e83f5989..cf6e90b5 100755
|
||||
--- a/setup.py
|
||||
+++ b/setup.py
|
||||
@@ -82,7 +82,8 @@ def set_openbsd_rc_files(data_files, dest="/etc/rc.d/", src=None):
|
||||
@ -50,6 +54,3 @@ index 8f5d92b4..f76aff30 100755
|
||||
"config/99-azure-product-uuid.rules"]
|
||||
data_files.append((dest, src))
|
||||
|
||||
--
|
||||
2.39.3
|
||||
|
||||
28
0100-add-oracle-support.patch
Normal file
28
0100-add-oracle-support.patch
Normal file
@ -0,0 +1,28 @@
|
||||
From 246586f31209b70667b494db30c847c5b87fe7a3 Mon Sep 17 00:00:00 2001
|
||||
From: Darren Archibald <darren.archibald@oracle.com>
|
||||
Date: Wed, 21 Sep 2022 05:40:34 -0700
|
||||
Subject: [PATCH] Add Oracle support
|
||||
|
||||
Add oracle support to fix waagent.service build issue
|
||||
|
||||
Signed-off-by: Darren Archibald <darren.archibald@oracle.com>
|
||||
---
|
||||
setup.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/setup.py b/setup.py
|
||||
index e4d7cf7..82ec9fe 100755
|
||||
--- a/setup.py
|
||||
+++ b/setup.py
|
||||
@@ -96,7 +96,7 @@
|
||||
systemd_dir_path = osutil.get_systemd_unit_file_install_path()
|
||||
agent_bin_path = osutil.get_agent_bin_path()
|
||||
|
||||
- if name in ('redhat', 'rhel', 'centos', 'almalinux', 'cloudlinux', 'rocky'):
|
||||
+ if name in ('redhat', 'rhel', 'centos', 'almalinux', 'cloudlinux', 'rocky', 'oracle'):
|
||||
if version.startswith("8") or version.startswith("9"):
|
||||
# redhat8+ default to py3
|
||||
set_bin_files(data_files, dest=agent_bin_path,
|
||||
--
|
||||
2.27.0
|
||||
|
||||
@ -2,24 +2,25 @@
|
||||
%global dracut_modname 97walinuxagent
|
||||
|
||||
Name: WALinuxAgent
|
||||
Version: 2.9.1.1
|
||||
Release: 9%{?dist}.1
|
||||
Version: 2.13.1.1
|
||||
Release: 2.0.1%{?dist}.1
|
||||
Summary: The Microsoft Azure Linux Agent
|
||||
|
||||
License: Apache-2.0
|
||||
URL: https://github.com/Azure/%{name}
|
||||
Source0: https://github.com/Azure/%{name}/archive/v%{version}.tar.gz
|
||||
Source1: module-setup.sh
|
||||
|
||||
Patch1: 0001-waagent.service-set-ConditionVirtualization-microsof.patch
|
||||
# For RHEL-35963 - [Azure][WALA] Consider to disable Log collector [rhel-10]
|
||||
Patch2: wla-Disable-automatic-log-collector.patch
|
||||
# For RHEL-40966 - [Azure][WALA][RHEL-10] Provisioning failed if no ifcfg-eth0
|
||||
Patch3: wla-redhat-Use-NetworkManager-to-set-DHCP-hostnames-on-r.patch
|
||||
# For RHEL-46713 - [Azure][RHEL-10][WALA] waagent -collect-logs doesn't work and the log is confusing
|
||||
Patch4: wla-skip-cgorup-monitor-2939.patch
|
||||
# For RHEL-68796 - Please add `mana` to 99-azure-unmanaged-devices.conf of Azure image
|
||||
Patch5: wla-redhat-Add-a-udev-rule-to-avoid-managing-slave-NICs-.patch
|
||||
Patch1: 0001-waagent.service-set-ConditionVirtualization-microsof.patch
|
||||
Patch2: 0002-Disable-automatic-log-collector.patch
|
||||
Patch3: 0003-redhat-Use-NetworkManager-to-set-DHCP-hostnames-on-r.patch
|
||||
Patch4: 0004-redhat-Add-a-udev-rule-to-avoid-managing-slave-NICs-.patch
|
||||
# For RHEL-109465 - [Azure][RHEL-10][WALA][Image mode] Cannot find 'service' command
|
||||
Patch5: wla-Use-systemctl-instead-of-service-to-manager-services.patch
|
||||
# For RHEL-96792 - [Azure][WALA][RHEL-10] Missing man page
|
||||
Patch6: wla-docs-add-waagent-manpage-3401.patch
|
||||
# For RHEL-129954 - Update walagent to 2.14 to support FIPS 140-3 on Azure [rhel-10.1.z]
|
||||
Patch7: wla-Jira-https-issues.redhat.com-browse-RHEL-129954.patch
|
||||
Patch1000: 0100-add-oracle-support.patch
|
||||
|
||||
BuildArch: noarch
|
||||
|
||||
@ -109,6 +110,7 @@ rm -rf %{_unitdir}/waagent.service.d/
|
||||
|
||||
%files
|
||||
%doc LICENSE.txt NOTICE README.md
|
||||
%{_mandir}/man1/waagent.1.gz
|
||||
%ghost %{_localstatedir}/log/waagent.log
|
||||
%ghost %{_unitdir}/waagent-network-setup.service
|
||||
%dir %attr(0700, root, root) %{_sharedstatedir}/waagent
|
||||
@ -131,7 +133,28 @@ rm -rf %{_unitdir}/waagent.service.d/
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Tue Mar 25 2025 Miroslav Rezanina <mrezanin@redhat.com> - 2.9.1.1-9.1
|
||||
* Tue Jan 06 2026 EL Errata <el-errata_ww@oracle.com> - 2.13.1.1-2.0.1
|
||||
- Add oracle support to fix waagent.service build issue
|
||||
|
||||
* Fri Nov 28 2025 Miroslav Rezanina <mrezanin@redhat.com> - 2.13.1.1-2.el10_1.1
|
||||
- wla-Jira-https-issues.redhat.com-browse-RHEL-129954.patch [RHEL-129954]
|
||||
- Resolves: RHEL-129954
|
||||
(Update walagent to 2.14 to support FIPS 140-3 on Azure [rhel-10.1.z])
|
||||
|
||||
* Thu Aug 21 2025 Miroslav Rezanina <mrezanin@redhat.com> - 2.13.1.1-2
|
||||
- wla-Use-systemctl-instead-of-service-to-manager-services.patch [RHEL-109465]
|
||||
- wla-docs-add-waagent-manpage-3401.patch [RHEL-96792]
|
||||
- Resolves: RHEL-109465
|
||||
([Azure][RHEL-10][WALA][Image mode] Cannot find 'service' command)
|
||||
- Resolves: RHEL-96792
|
||||
([Azure][WALA][RHEL-10] Missing man page)
|
||||
|
||||
* Thu May 22 2025 Vitaly Kuznetsov <vkuznets@redhat.com> - 2.13.1.1-1
|
||||
- Rebase to 2.13.1.1 [RHEL-86509]
|
||||
- Resolves: RHEL-86509
|
||||
(Rebase to v2.13.1.1)
|
||||
|
||||
* Tue Mar 25 2025 Miroslav Rezanina <mrezanin@redhat.com> - 2.9.1.1-10
|
||||
- wla-redhat-Explicitly-list-udev-rule-requirements-in-the.patch [RHEL-84073]
|
||||
- wla-redhat-Include-10-azure-unmanaged-sriov.rules-into-i.patch [RHEL-84073]
|
||||
- Resolves: RHEL-84073
|
||||
|
||||
2
sources
2
sources
@ -1 +1 @@
|
||||
3f44aecc16ac545db4b550586f168dbbdef34289aad6775973517bf645e5a1d486864c01e974f03a71b3e946c14e1ca140673a75c1cd602aac28725eaa68e83d v2.9.1.1.tar.gz
|
||||
SHA512 (v2.13.1.1.tar.gz) = 3cb65495955c746bf112e794cbeb11f47ace72e4272c3cd16eb8d478c0b3b0323890b52c592b68775efafb8c6f267b3614e66f09d6a6dee066b603297676cd38
|
||||
|
||||
706
wla-Jira-https-issues.redhat.com-browse-RHEL-129954.patch
Normal file
706
wla-Jira-https-issues.redhat.com-browse-RHEL-129954.patch
Normal file
@ -0,0 +1,706 @@
|
||||
From 0e90372ba24091860266bb0a3c33fc20e38a1a97 Mon Sep 17 00:00:00 2001
|
||||
From: Norberto Arrieta <narrieta@users.noreply.github.com>
|
||||
Date: Tue, 4 Mar 2025 12:55:27 -0800
|
||||
Subject: [PATCH] Jira: https://issues.redhat.com/browse/RHEL-129954
|
||||
|
||||
RH-Author: yuxisun <None>
|
||||
RH-MergeRequest: 23: Support for FIPS 140-3 (#3324)
|
||||
RH-Jira: RHEL-129954
|
||||
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/1] da147f85a89d1375c0f4d7e36fffd0f68b231770
|
||||
|
||||
Support for FIPS 140-3 (#3324)
|
||||
|
||||
When fetching certificates from WireServer, the Agent uses DES_EDE3_CBC. The PFX it receives has a MAC computed using PKCS12KDF. Both are deprecated on FIPS 140-3.
|
||||
This PR switches to AES128_CBC for communication with the WireServer (a subsequent PR will change it to AES256_CBC) and skips MAC verification when it is not needed.
|
||||
The changes also include some minor cleanup to remove data structures that are not used.
|
||||
|
||||
Upstream PR: https://github.com/Azure/WALinuxAgent/pull/3324
|
||||
|
||||
Signed-off-by: Yuxin Sun <yuxisun@redhat.com>
|
||||
---
|
||||
azurelinuxagent/common/event.py | 20 ++
|
||||
azurelinuxagent/common/protocol/goal_state.py | 216 +++++++++++-------
|
||||
azurelinuxagent/common/protocol/restapi.py | 24 --
|
||||
azurelinuxagent/common/protocol/wire.py | 18 +-
|
||||
azurelinuxagent/common/utils/cryptutil.py | 46 ++--
|
||||
azurelinuxagent/ga/update.py | 1 +
|
||||
tests/common/protocol/test_goal_state.py | 81 ++++++-
|
||||
tests/common/protocol/test_hostplugin.py | 35 ++-
|
||||
tests/common/protocol/test_wire.py | 8 +-
|
||||
tests/ga/test_update.py | 2 +-
|
||||
10 files changed, 275 insertions(+), 176 deletions(-)
|
||||
|
||||
diff --git a/azurelinuxagent/common/event.py b/azurelinuxagent/common/event.py
|
||||
index 6b9521ca..9b8a926e 100644
|
||||
--- a/azurelinuxagent/common/event.py
|
||||
+++ b/azurelinuxagent/common/event.py
|
||||
@@ -93,6 +93,7 @@ class WALAEventOperation:
|
||||
FetchGoalState = "FetchGoalState"
|
||||
Firewall = "Firewall"
|
||||
GoalState = "GoalState"
|
||||
+ GoalStateCertificates = "GoalStateCertificates"
|
||||
GoalStateUnsupportedFeatures = "GoalStateUnsupportedFeatures"
|
||||
HealthCheck = "HealthCheck"
|
||||
HealthObservation = "HealthObservation"
|
||||
@@ -733,6 +734,25 @@ def error(op, fmt, *args):
|
||||
add_event(op=op, message=fmt.format(*args), is_success=False, log_event=False)
|
||||
|
||||
|
||||
+class LogEvent(object):
|
||||
+ """
|
||||
+ Helper class that allows the use of info()/warn()/error() using a specific instance of a logger.
|
||||
+ """
|
||||
+ def __init__(self, logger_):
|
||||
+ self._logger = logger_
|
||||
+
|
||||
+ def info(self, op, fmt, *args):
|
||||
+ self._logger.info(fmt, *args)
|
||||
+ add_event(op=op, message=fmt.format(*args), is_success=True)
|
||||
+
|
||||
+ def warn(self, op, fmt, *args):
|
||||
+ self._logger.warn(fmt, *args)
|
||||
+ add_event(op=op, message="[WARNING] " + fmt.format(*args), is_success=False, log_event=False)
|
||||
+
|
||||
+ def error(self, op, fmt, *args):
|
||||
+ self._logger.error(fmt, *args)
|
||||
+ add_event(op=op, message=fmt.format(*args), is_success=False, log_event=False)
|
||||
+
|
||||
def add_log_event(level, message, forced=False, reporter=__event_logger__):
|
||||
"""
|
||||
:param level: LoggerLevel of the log event
|
||||
diff --git a/azurelinuxagent/common/protocol/goal_state.py b/azurelinuxagent/common/protocol/goal_state.py
|
||||
index f94f3ae5..2556cc73 100644
|
||||
--- a/azurelinuxagent/common/protocol/goal_state.py
|
||||
+++ b/azurelinuxagent/common/protocol/goal_state.py
|
||||
@@ -24,15 +24,14 @@ import json
|
||||
from azurelinuxagent.common import conf
|
||||
from azurelinuxagent.common import logger
|
||||
from azurelinuxagent.common.AgentGlobals import AgentGlobals
|
||||
-from azurelinuxagent.common.datacontract import set_properties
|
||||
-from azurelinuxagent.common.event import add_event, WALAEventOperation
|
||||
+from azurelinuxagent.common.event import add_event, WALAEventOperation, LogEvent
|
||||
from azurelinuxagent.common.exception import ProtocolError, ResourceGoneError
|
||||
from azurelinuxagent.common.future import ustr
|
||||
from azurelinuxagent.common.protocol.extensions_goal_state_factory import ExtensionsGoalStateFactory
|
||||
from azurelinuxagent.common.protocol.extensions_goal_state import VmSettingsParseError, GoalStateSource
|
||||
from azurelinuxagent.common.protocol.hostplugin import VmSettingsNotSupported, VmSettingsSupportStopped
|
||||
-from azurelinuxagent.common.protocol.restapi import Cert, CertList, RemoteAccessUser, RemoteAccessUsersList, ExtHandlerPackage, ExtHandlerPackageList
|
||||
-from azurelinuxagent.common.utils import fileutil
|
||||
+from azurelinuxagent.common.protocol.restapi import RemoteAccessUser, RemoteAccessUsersList, ExtHandlerPackage, ExtHandlerPackageList
|
||||
+from azurelinuxagent.common.utils import fileutil, shellutil
|
||||
from azurelinuxagent.common.utils.archive import GoalStateHistory, SHARED_CONF_FILE_NAME
|
||||
from azurelinuxagent.common.utils.cryptutil import CryptUtil
|
||||
from azurelinuxagent.common.utils.textutil import parse_doc, findall, find, findtext, getattrib, gettext
|
||||
@@ -41,6 +40,7 @@ from azurelinuxagent.common.utils.textutil import parse_doc, findall, find, find
|
||||
GOAL_STATE_URI = "http://{0}/machine/?comp=goalstate"
|
||||
CERTS_FILE_NAME = "Certificates.xml"
|
||||
P7M_FILE_NAME = "Certificates.p7m"
|
||||
+PFX_FILE_NAME = "Certificates.pfx"
|
||||
PEM_FILE_NAME = "Certificates.pem"
|
||||
TRANSPORT_CERT_FILE_NAME = "TransportCert.pem"
|
||||
TRANSPORT_PRV_FILE_NAME = "TransportPrivate.pem"
|
||||
@@ -282,16 +282,8 @@ class GoalState(object):
|
||||
self._check_and_download_missing_certs_on_disk()
|
||||
|
||||
def _download_certificates(self, certs_uri):
|
||||
- xml_text = self._wire_client.fetch_config(certs_uri, self._wire_client.get_header_for_cert())
|
||||
- certs = Certificates(xml_text, self.logger)
|
||||
- # Log and save the certificates summary (i.e. the thumbprint but not the certificate itself) to the goal state history
|
||||
- for c in certs.summary:
|
||||
- message = "Downloaded certificate {0}".format(c)
|
||||
- self.logger.info(message)
|
||||
- add_event(op=WALAEventOperation.GoalState, message=message)
|
||||
- if len(certs.warnings) > 0:
|
||||
- self.logger.warn(certs.warnings)
|
||||
- add_event(op=WALAEventOperation.GoalState, message=certs.warnings)
|
||||
+ certs = Certificates(self._wire_client, certs_uri, self.logger)
|
||||
+ # Save the certificates summary (i.e. the thumbprints but not the certificates themselves) to the goal state history
|
||||
if self._save_to_history:
|
||||
self._history.save_certificates(json.dumps(certs.summary))
|
||||
return certs
|
||||
@@ -511,31 +503,83 @@ class SharedConfig(object):
|
||||
self.xml_text = xml_text
|
||||
|
||||
|
||||
-class Certificates(object):
|
||||
- def __init__(self, xml_text, my_logger):
|
||||
- self.cert_list = CertList()
|
||||
- self.summary = [] # debugging info
|
||||
- self.warnings = []
|
||||
+class Certificates(LogEvent):
|
||||
+ def __init__(self, wire_client, uri, logger_):
|
||||
+ super(Certificates, self).__init__(logger_)
|
||||
+ self.summary = []
|
||||
+ self._crypt_util = CryptUtil(conf.get_openssl_cmd())
|
||||
|
||||
- # Save the certificates
|
||||
- local_file = os.path.join(conf.get_lib_dir(), CERTS_FILE_NAME)
|
||||
- fileutil.write_file(local_file, xml_text)
|
||||
+ try:
|
||||
+ pfx_file = self._download_certificates_pfx(wire_client, uri)
|
||||
+ if pfx_file is None: # The response from the WireServer may not have any certificates
|
||||
+ return
|
||||
|
||||
- # Separate the certificates into individual files.
|
||||
- xml_doc = parse_doc(xml_text)
|
||||
- data = findtext(xml_doc, "Data")
|
||||
- if data is None:
|
||||
- return
|
||||
+ try:
|
||||
+ pem_file = self._convert_certificates_pfx_to_pem(pfx_file)
|
||||
+ finally:
|
||||
+ self._remove_file(pfx_file)
|
||||
|
||||
- # if the certificates format is not Pkcs7BlobWithPfxContents do not parse it
|
||||
- certificate_format = findtext(xml_doc, "Format")
|
||||
- if certificate_format and certificate_format != "Pkcs7BlobWithPfxContents":
|
||||
- message = "The Format is not Pkcs7BlobWithPfxContents. Format is {0}".format(certificate_format)
|
||||
- my_logger.warn(message)
|
||||
- add_event(op=WALAEventOperation.GoalState, message=message)
|
||||
- return
|
||||
+ self.summary = self._extract_certificate(pem_file)
|
||||
+
|
||||
+ for c in self.summary:
|
||||
+ self.info(WALAEventOperation.GoalStateCertificates, "Downloaded certificate {0}", c)
|
||||
+
|
||||
+ except Exception as e:
|
||||
+ self.error(WALAEventOperation.GoalStateCertificates, "Error fetching the goal state certificates: {0}", ustr(e))
|
||||
+
|
||||
+ def _remove_file(self, file):
|
||||
+ if os.path.exists(file):
|
||||
+ try:
|
||||
+ os.remove(file)
|
||||
+ except Exception as e:
|
||||
+ self.warn(WALAEventOperation.GoalStateCertificates, "Failed to remove {0}: {1}", file, ustr(e))
|
||||
+
|
||||
+ def _download_certificates_pfx(self, wire_client, uri):
|
||||
+ """
|
||||
+ Downloads the certificates from the WireServer and saves them to a pfx file.
|
||||
+ Returns the full path of the pfx file, or None, if the WireServer response does not have a "Data" element
|
||||
+ """
|
||||
+ trans_prv_file = os.path.join(conf.get_lib_dir(), TRANSPORT_PRV_FILE_NAME)
|
||||
+ trans_cert_file = os.path.join(conf.get_lib_dir(), TRANSPORT_CERT_FILE_NAME)
|
||||
+ xml_file = os.path.join(conf.get_lib_dir(), CERTS_FILE_NAME)
|
||||
+ pfx_file = os.path.join(conf.get_lib_dir(), PFX_FILE_NAME)
|
||||
+
|
||||
+ for cypher in ["AES128_CBC", "DES_EDE3_CBC"]:
|
||||
+ headers = wire_client.get_headers_for_encrypted_request(cypher)
|
||||
+
|
||||
+ try:
|
||||
+ xml_text = wire_client.fetch_config(uri, headers)
|
||||
+ except Exception as e:
|
||||
+ self.warn(WALAEventOperation.GoalStateCertificates, "Error in Certificates request [cypher: {0}]: {1}", cypher, ustr(e))
|
||||
+ continue
|
||||
|
||||
- cryptutil = CryptUtil(conf.get_openssl_cmd())
|
||||
+ fileutil.write_file(xml_file, xml_text)
|
||||
+
|
||||
+ xml_doc = parse_doc(xml_text)
|
||||
+ data = findtext(xml_doc, "Data")
|
||||
+ if data is None:
|
||||
+ self.info(WALAEventOperation.GoalStateCertificates, "The Data element of the Certificates response is empty")
|
||||
+ return None
|
||||
+ certificate_format = findtext(xml_doc, "Format")
|
||||
+ if certificate_format and certificate_format != "Pkcs7BlobWithPfxContents":
|
||||
+ self.warn(WALAEventOperation.GoalStateCertificates, "The Certificates format is not Pkcs7BlobWithPfxContents; skipping. Format is {0}", certificate_format)
|
||||
+ return None
|
||||
+
|
||||
+ p7m_file = Certificates._create_p7m_file(data)
|
||||
+
|
||||
+ try:
|
||||
+ self._crypt_util.decrypt_certificates_p7m(p7m_file, trans_prv_file, trans_cert_file, pfx_file)
|
||||
+ except shellutil.CommandError as e:
|
||||
+ self.warn(WALAEventOperation.GoalState, "Error in transport decryption [cypher: {0}]: {1}", cypher, ustr(e))
|
||||
+ self._remove_file(pfx_file)
|
||||
+ continue
|
||||
+
|
||||
+ return pfx_file
|
||||
+
|
||||
+ raise Exception("Cannot download certificates using any of the supported cyphers")
|
||||
+
|
||||
+ @staticmethod
|
||||
+ def _create_p7m_file(data):
|
||||
p7m_file = os.path.join(conf.get_lib_dir(), P7M_FILE_NAME)
|
||||
p7m = ("MIME-Version:1.0\n" # pylint: disable=W1308
|
||||
"Content-Disposition: attachment; filename=\"{0}\"\n"
|
||||
@@ -543,68 +587,72 @@ class Certificates(object):
|
||||
"Content-Transfer-Encoding: base64\n"
|
||||
"\n"
|
||||
"{2}").format(p7m_file, p7m_file, data)
|
||||
-
|
||||
fileutil.write_file(p7m_file, p7m)
|
||||
+ return p7m_file
|
||||
|
||||
- trans_prv_file = os.path.join(conf.get_lib_dir(), TRANSPORT_PRV_FILE_NAME)
|
||||
- trans_cert_file = os.path.join(conf.get_lib_dir(), TRANSPORT_CERT_FILE_NAME)
|
||||
+ def _convert_certificates_pfx_to_pem(self, pfx_file):
|
||||
+ """
|
||||
+ Convert the pfx file to pem file.
|
||||
+ """
|
||||
pem_file = os.path.join(conf.get_lib_dir(), PEM_FILE_NAME)
|
||||
- # decrypt certificates
|
||||
- cryptutil.decrypt_p7m(p7m_file, trans_prv_file, trans_cert_file, pem_file)
|
||||
|
||||
+ for nomacver in [True, False]:
|
||||
+ try:
|
||||
+ self._crypt_util.convert_pfx_to_pem(pfx_file, nomacver, pem_file)
|
||||
+ return pem_file
|
||||
+ except shellutil.CommandError as e:
|
||||
+ self._remove_file(pem_file) # An error may leave an empty pem file, which can produce a failure on some versions of open SSL (e.g. 3.2.2) on the next invocation
|
||||
+ self.warn(WALAEventOperation.GoalState, "Error converting PFX to PEM [-nomacver: {0}]: {1}", nomacver, ustr(e))
|
||||
+ continue
|
||||
+
|
||||
+ raise Exception("Cannot convert PFX to PEM")
|
||||
+
|
||||
+ def _extract_certificate(self, pem_file):
|
||||
+ """
|
||||
+ Parse the certificates and private keys from the pem file and store them in the certificates directory.
|
||||
+ """
|
||||
# The parsing process use public key to match prv and crt.
|
||||
- buf = []
|
||||
- prvs = {}
|
||||
- thumbprints = {}
|
||||
+ private_keys = {} # map of private keys indexed by public key
|
||||
+ thumbprints = {} # map of thumbprints indexed by public key
|
||||
+ buffer = [] # buffer for reading lines belonging to a certificate or private key
|
||||
index = 0
|
||||
- v1_cert_list = []
|
||||
-
|
||||
- # Ensure pem_file exists before read the certs data since decrypt_p7m may clear the pem_file wen decryption fails
|
||||
- if os.path.exists(pem_file):
|
||||
- with open(pem_file) as pem:
|
||||
- for line in pem.readlines():
|
||||
- buf.append(line)
|
||||
- if re.match(r'[-]+END.*KEY[-]+', line):
|
||||
- tmp_file = Certificates._write_to_tmp_file(index, 'prv', buf)
|
||||
- pub = cryptutil.get_pubkey_from_prv(tmp_file)
|
||||
- prvs[pub] = tmp_file
|
||||
- buf = []
|
||||
- index += 1
|
||||
- elif re.match(r'[-]+END.*CERTIFICATE[-]+', line):
|
||||
- tmp_file = Certificates._write_to_tmp_file(index, 'crt', buf)
|
||||
- pub = cryptutil.get_pubkey_from_crt(tmp_file)
|
||||
- thumbprint = cryptutil.get_thumbprint_from_crt(tmp_file)
|
||||
- thumbprints[pub] = thumbprint
|
||||
- # Rename crt with thumbprint as the file name
|
||||
- crt = "{0}.crt".format(thumbprint)
|
||||
- v1_cert_list.append({
|
||||
- "name": None,
|
||||
- "thumbprint": thumbprint
|
||||
- })
|
||||
- os.rename(tmp_file, os.path.join(conf.get_lib_dir(), crt))
|
||||
- buf = []
|
||||
- index += 1
|
||||
+
|
||||
+ with open(pem_file) as pem:
|
||||
+ for line in pem.readlines():
|
||||
+ buffer.append(line)
|
||||
+ if re.match(r'[-]+END.*KEY[-]+', line):
|
||||
+ tmp_file = Certificates._write_to_tmp_file(index, 'prv', buffer)
|
||||
+ pub = self._crypt_util.get_pubkey_from_prv(tmp_file)
|
||||
+ private_keys[pub] = tmp_file
|
||||
+ buffer = []
|
||||
+ index += 1
|
||||
+ elif re.match(r'[-]+END.*CERTIFICATE[-]+', line):
|
||||
+ tmp_file = Certificates._write_to_tmp_file(index, 'crt', buffer)
|
||||
+ pub = self._crypt_util.get_pubkey_from_crt(tmp_file)
|
||||
+ thumbprint = self._crypt_util.get_thumbprint_from_crt(tmp_file)
|
||||
+ thumbprints[pub] = thumbprint
|
||||
+ # Rename crt with thumbprint as the file name
|
||||
+ crt = "{0}.crt".format(thumbprint)
|
||||
+ os.rename(tmp_file, os.path.join(conf.get_lib_dir(), crt))
|
||||
+ buffer = []
|
||||
+ index += 1
|
||||
|
||||
# Rename prv key with thumbprint as the file name
|
||||
- for pubkey in prvs:
|
||||
+ for pubkey in private_keys:
|
||||
thumbprint = thumbprints[pubkey]
|
||||
if thumbprint:
|
||||
- tmp_file = prvs[pubkey]
|
||||
+ tmp_file = private_keys[pubkey]
|
||||
prv = "{0}.prv".format(thumbprint)
|
||||
os.rename(tmp_file, os.path.join(conf.get_lib_dir(), prv))
|
||||
else:
|
||||
- # Since private key has *no* matching certificate,
|
||||
- # it will not be named correctly
|
||||
- self.warnings.append("Found NO matching cert/thumbprint for private key!")
|
||||
+ # Since private key has *no* matching certificate, it will not be named correctly
|
||||
+ self.warn(WALAEventOperation.GoalState, "Found a private key with no matching cert/thumbprint!")
|
||||
|
||||
+ certificates = []
|
||||
for pubkey, thumbprint in thumbprints.items():
|
||||
- has_private_key = pubkey in prvs
|
||||
- self.summary.append({"thumbprint": thumbprint, "hasPrivateKey": has_private_key})
|
||||
-
|
||||
- for v1_cert in v1_cert_list:
|
||||
- cert = Cert()
|
||||
- set_properties("certs", cert, v1_cert)
|
||||
- self.cert_list.certificates.append(cert)
|
||||
+ has_private_key = pubkey in private_keys
|
||||
+ certificates.append({"thumbprint": thumbprint, "hasPrivateKey": has_private_key})
|
||||
+ return certificates
|
||||
|
||||
@staticmethod
|
||||
def _write_to_tmp_file(index, suffix, buf):
|
||||
@@ -614,9 +662,7 @@ class Certificates(object):
|
||||
|
||||
class EmptyCertificates:
|
||||
def __init__(self):
|
||||
- self.cert_list = CertList()
|
||||
- self.summary = [] # debugging info
|
||||
- self.warnings = []
|
||||
+ self.summary = []
|
||||
|
||||
class RemoteAccess(object):
|
||||
"""
|
||||
diff --git a/azurelinuxagent/common/protocol/restapi.py b/azurelinuxagent/common/protocol/restapi.py
|
||||
index 54e020c1..7e563b4a 100644
|
||||
--- a/azurelinuxagent/common/protocol/restapi.py
|
||||
+++ b/azurelinuxagent/common/protocol/restapi.py
|
||||
@@ -43,30 +43,6 @@ class VMInfo(DataContract):
|
||||
self.tenantName = tenantName
|
||||
|
||||
|
||||
-class CertificateData(DataContract):
|
||||
- def __init__(self, certificateData=None):
|
||||
- self.certificateData = certificateData
|
||||
-
|
||||
-
|
||||
-class Cert(DataContract):
|
||||
- def __init__(self,
|
||||
- name=None,
|
||||
- thumbprint=None,
|
||||
- certificateDataUri=None,
|
||||
- storeName=None,
|
||||
- storeLocation=None):
|
||||
- self.name = name
|
||||
- self.thumbprint = thumbprint
|
||||
- self.certificateDataUri = certificateDataUri
|
||||
- self.storeLocation = storeLocation
|
||||
- self.storeName = storeName
|
||||
-
|
||||
-
|
||||
-class CertList(DataContract):
|
||||
- def __init__(self):
|
||||
- self.certificates = DataContractList(Cert)
|
||||
-
|
||||
-
|
||||
class VMAgentFamily(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
diff --git a/azurelinuxagent/common/protocol/wire.py b/azurelinuxagent/common/protocol/wire.py
|
||||
index 00a01f09..0277b7f0 100644
|
||||
--- a/azurelinuxagent/common/protocol/wire.py
|
||||
+++ b/azurelinuxagent/common/protocol/wire.py
|
||||
@@ -115,8 +115,7 @@ class WireProtocol(DataContract):
|
||||
return vminfo
|
||||
|
||||
def get_certs(self):
|
||||
- certificates = self.client.get_certs()
|
||||
- return certificates.cert_list
|
||||
+ return self.client.get_certs()
|
||||
|
||||
def get_goal_state(self):
|
||||
return self.client.get_goal_state()
|
||||
@@ -1140,13 +1139,11 @@ class WireClient(object):
|
||||
"Content-Type": "text/xml;charset=utf-8"
|
||||
}
|
||||
|
||||
- def get_header_for_cert(self):
|
||||
- return self._get_header_for_encrypted_request("DES_EDE3_CBC")
|
||||
-
|
||||
def get_header_for_remote_access(self):
|
||||
- return self._get_header_for_encrypted_request("AES128_CBC")
|
||||
+ return self.get_headers_for_encrypted_request("AES128_CBC")
|
||||
|
||||
- def _get_header_for_encrypted_request(self, cypher):
|
||||
+ @staticmethod
|
||||
+ def get_headers_for_encrypted_request(cypher):
|
||||
trans_cert_file = os.path.join(conf.get_lib_dir(), TRANSPORT_CERT_FILE_NAME)
|
||||
try:
|
||||
content = fileutil.read_file(trans_cert_file)
|
||||
@@ -1154,12 +1151,15 @@ class WireClient(object):
|
||||
raise ProtocolError("Failed to read {0}: {1}".format(trans_cert_file, e))
|
||||
|
||||
cert = get_bytes_from_pem(content)
|
||||
- return {
|
||||
+ headers = {
|
||||
"x-ms-agent-name": "WALinuxAgent",
|
||||
"x-ms-version": PROTOCOL_VERSION,
|
||||
- "x-ms-cipher-name": cypher,
|
||||
"x-ms-guest-agent-public-x509-cert": cert
|
||||
}
|
||||
+ if cypher is not None: # the cypher header is optional, currently defaults to AES128_CBC
|
||||
+ headers["x-ms-cipher-name"] = cypher
|
||||
+
|
||||
+ return headers
|
||||
|
||||
def get_host_plugin(self):
|
||||
if self._host_plugin is None:
|
||||
diff --git a/azurelinuxagent/common/utils/cryptutil.py b/azurelinuxagent/common/utils/cryptutil.py
|
||||
index 00126e25..789a9486 100644
|
||||
--- a/azurelinuxagent/common/utils/cryptutil.py
|
||||
+++ b/azurelinuxagent/common/utils/cryptutil.py
|
||||
@@ -86,36 +86,22 @@ class CryptUtil(object):
|
||||
thumbprint = thumbprint.rstrip().split('=')[1].replace(':', '').upper()
|
||||
return thumbprint
|
||||
|
||||
- def decrypt_p7m(self, p7m_file, trans_prv_file, trans_cert_file, pem_file):
|
||||
-
|
||||
- def _cleanup_files(files_to_cleanup):
|
||||
- for file_path in files_to_cleanup:
|
||||
- if os.path.exists(file_path):
|
||||
- try:
|
||||
- os.remove(file_path)
|
||||
- logger.info("Removed file {0}", file_path)
|
||||
- except Exception as e:
|
||||
- logger.error("Failed to remove file {0}: {1}", file_path, ustr(e))
|
||||
-
|
||||
- if not os.path.exists(p7m_file):
|
||||
- raise IOError(errno.ENOENT, "File not found", p7m_file)
|
||||
- elif not os.path.exists(trans_prv_file):
|
||||
- raise IOError(errno.ENOENT, "File not found", trans_prv_file)
|
||||
- else:
|
||||
- try:
|
||||
- shellutil.run_pipe([
|
||||
- [self.openssl_cmd, "cms", "-decrypt", "-in", p7m_file, "-inkey", trans_prv_file, "-recip", trans_cert_file],
|
||||
- [self.openssl_cmd, "pkcs12", "-nodes", "-password", "pass:", "-out", pem_file]])
|
||||
- except shellutil.CommandError as command_error:
|
||||
- logger.error("Failed to decrypt {0} (return code: {1})\n[stdout]\n{2}\n[stderr]\n{3}",
|
||||
- p7m_file, command_error.returncode, command_error.stdout, command_error.stderr)
|
||||
- # If the decryption fails, old version of openssl overwrite the output file(if exist) with empty data while
|
||||
- # new version of openssl(3.2.2) does not overwrite the output file, So output file may contain old certs data.
|
||||
- # Correcting the behavior by removing the temporary output files since having empty/no data is makes sense when decryption fails
|
||||
- # otherwise we end up processing old certs again.
|
||||
- files_to_remove = [p7m_file, pem_file]
|
||||
- logger.info("Removing temporary state certificate files {0}", files_to_remove)
|
||||
- _cleanup_files(files_to_remove)
|
||||
+ def decrypt_certificates_p7m(self, p7m_file, trans_prv_file, trans_cert_file, pfx_file):
|
||||
+ umask = None
|
||||
+ try:
|
||||
+ umask = os.umask(0o077)
|
||||
+ with open(pfx_file, "wb") as pfx_file_:
|
||||
+ shellutil.run_command([self.openssl_cmd, "cms", "-decrypt", "-in", p7m_file, "-inkey", trans_prv_file, "-recip", trans_cert_file], stdout=pfx_file_)
|
||||
+ finally:
|
||||
+ if umask is not None:
|
||||
+ os.umask(umask)
|
||||
+
|
||||
+ def convert_pfx_to_pem(self, pfx_file, nomacver, pem_file):
|
||||
+ command = [self.openssl_cmd, "pkcs12", "-nodes", "-password", "pass:", "-in", pfx_file, "-out", pem_file]
|
||||
+ if nomacver:
|
||||
+ command.append("-nomacver")
|
||||
+
|
||||
+ shellutil.run_command(command)
|
||||
|
||||
def crt_to_ssh(self, input_file, output_file):
|
||||
with open(output_file, "ab") as file_out:
|
||||
diff --git a/azurelinuxagent/ga/update.py b/azurelinuxagent/ga/update.py
|
||||
index 7ab19101..f806ff26 100644
|
||||
--- a/azurelinuxagent/ga/update.py
|
||||
+++ b/azurelinuxagent/ga/update.py
|
||||
@@ -85,6 +85,7 @@ READONLY_FILE_GLOBS = [
|
||||
"*.p7m",
|
||||
"*.pem",
|
||||
"*.prv",
|
||||
+ "Certificates.xml",
|
||||
"ovf-env.xml"
|
||||
]
|
||||
|
||||
diff --git a/tests/common/protocol/test_goal_state.py b/tests/common/protocol/test_goal_state.py
|
||||
index a5f89587..9b70ce05 100644
|
||||
--- a/tests/common/protocol/test_goal_state.py
|
||||
+++ b/tests/common/protocol/test_goal_state.py
|
||||
@@ -6,6 +6,7 @@ import datetime
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
+import subprocess
|
||||
import shutil
|
||||
import time
|
||||
|
||||
@@ -492,9 +493,85 @@ class GoalStateTestCase(AgentTestCase, HttpRequestPredicates):
|
||||
|
||||
goal_state = GoalState(protocol.client)
|
||||
|
||||
- self.assertEqual(0, len(goal_state.certs.summary), "Cert list should be empty")
|
||||
- self.assertEqual(1, http_get_handler.certificate_requests, "There should have been exactly 1 requests for the goal state certificates")
|
||||
+ self.assertEqual(0, len(goal_state.certs.summary), "Certificates should be empty")
|
||||
+ self.assertEqual(2, http_get_handler.certificate_requests, "There should have been exactly 2 requests for the goal state certificates") # 1 for the initial request, 1 for the retry with an older cypher
|
||||
|
||||
+ def test_goal_state_should_try_legacy_cypher_and_then_fail_when_no_cyphers_are_supported_by_the_wireserver(self):
|
||||
+ cyphers = []
|
||||
+ def http_get_handler(url, *_, **kwargs):
|
||||
+ if HttpRequestPredicates.is_certificates_request(url):
|
||||
+ cypher = kwargs["headers"].get("x-ms-cipher-name")
|
||||
+ if cypher is None:
|
||||
+ raise Exception("x-ms-cipher-name header is missing from the Certificates request")
|
||||
+ cyphers.append(cypher)
|
||||
+ return MockHttpResponse(status=400, body="unsupported cypher: {0}".format(cypher).encode('utf-8'))
|
||||
+ return None
|
||||
+
|
||||
+ with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
|
||||
+ with patch("azurelinuxagent.common.event.LogEvent.error") as log_error_patch:
|
||||
+ protocol.set_http_handlers(http_get_handler=http_get_handler)
|
||||
+ goal_state = GoalState(protocol.client)
|
||||
+
|
||||
+ log_error_args, _ = log_error_patch.call_args
|
||||
+
|
||||
+ self.assertEqual(cyphers, ["AES128_CBC", "DES_EDE3_CBC"], "There should have been 2 requests for the goal state certificates (AES128_CBC and DES_EDE3_CBC)")
|
||||
+ self.assertEqual(log_error_args[0], "GoalStateCertificates", "An error fetching the goal state Certificates should have been reported")
|
||||
+ self.assertEqual(0, len(goal_state.certs.summary), "Certificates should be empty")
|
||||
+ self.assertFalse(os.path.exists(os.path.join(conf.get_lib_dir(), "Certificates.pfx")), "The Certificates.pfx file should not have been created")
|
||||
+
|
||||
+ def test_goal_state_should_try_legacy_cypher_and_then_fail_when_no_cyphers_are_supported_by_openssl(self):
|
||||
+ cyphers = []
|
||||
+ def http_get_handler(url, *_, **kwargs):
|
||||
+ if HttpRequestPredicates.is_certificates_request(url):
|
||||
+ cyphers.append(kwargs["headers"].get("x-ms-cipher-name"))
|
||||
+ return None
|
||||
+
|
||||
+ original_popen = subprocess.Popen
|
||||
+ openssl = conf.get_openssl_cmd()
|
||||
+ decrypt_calls = []
|
||||
+ def mock_fail_popen(command, *args, **kwargs):
|
||||
+ if len(command) > 3 and command[0:3] == [openssl, "cms", "-decrypt"]:
|
||||
+ decrypt_calls.append(command)
|
||||
+ command[1] = "fake_openssl_command" # force an error on the openssl to simulate a decryption failure
|
||||
+ return original_popen(command, *args, **kwargs)
|
||||
+
|
||||
+ with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
|
||||
+ protocol.set_http_handlers(http_get_handler=http_get_handler)
|
||||
+ with patch("azurelinuxagent.common.event.LogEvent.error") as log_error_patch:
|
||||
+ with patch("azurelinuxagent.ga.cgroupapi.subprocess.Popen", mock_fail_popen):
|
||||
+ goal_state = GoalState(protocol.client)
|
||||
+
|
||||
+ log_error_args, _ = log_error_patch.call_args
|
||||
+
|
||||
+ self.assertEqual(cyphers, ["AES128_CBC", "DES_EDE3_CBC"], "There should have been 2 requests for the goal state certificates (AES128_CBC and DES_EDE3_CBC)")
|
||||
+ self.assertEqual(2, len(decrypt_calls), "There should have been 2 calls to 'openssl cms -decrypt'")
|
||||
+ self.assertEqual(log_error_args[0], "GoalStateCertificates", "An error fetching the goal state Certificates should have been reported")
|
||||
+ self.assertEqual(0, len(goal_state.certs.summary), "Certificates should be empty")
|
||||
+ self.assertFalse(os.path.exists(os.path.join(conf.get_lib_dir(), "Certificates.pfx")), "The Certificates.pfx file should not have been created")
|
||||
+
|
||||
+ def test_goal_state_should_try_without_and_with_mac_verification_then_fail_when_the_pfx_cannot_be_converted(self):
|
||||
+ original_popen = subprocess.Popen
|
||||
+ openssl = conf.get_openssl_cmd()
|
||||
+ nomacver = []
|
||||
+
|
||||
+ def mock_fail_popen(command, *args, **kwargs):
|
||||
+ if len(command) > 2 and command[0] == openssl and command[1] == "pkcs12":
|
||||
+ nomacver.append("-nomacver" in command)
|
||||
+ # force an error on the openssl to simulate the conversion failure
|
||||
+ command[1] = "fake_openssl_command"
|
||||
+ return original_popen(command, *args, **kwargs)
|
||||
+
|
||||
+
|
||||
+ with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
|
||||
+ with patch("azurelinuxagent.common.event.LogEvent.error") as log_error_patch:
|
||||
+ with patch("azurelinuxagent.ga.cgroupapi.subprocess.Popen", mock_fail_popen):
|
||||
+ goal_state = GoalState(protocol.client)
|
||||
+
|
||||
+ log_error_args, _ = log_error_patch.call_args
|
||||
+
|
||||
+ self.assertEqual(nomacver, [True, False], "There should have been 2 attempts to parse the PFX (with and without -nomacver)")
|
||||
+ self.assertEqual(log_error_args[0], "GoalStateCertificates", "An error fetching the goal state Certificates should have been reported")
|
||||
+ self.assertEqual(0, len(goal_state.certs.summary), "Certificates should be empty")
|
||||
|
||||
def test_it_should_raise_when_goal_state_properties_not_initialized(self):
|
||||
with GoalStateTestCase._create_protocol_ws_and_hgap_in_sync() as protocol:
|
||||
diff --git a/tests/common/protocol/test_hostplugin.py b/tests/common/protocol/test_hostplugin.py
|
||||
index 4c97c73f..7d94139b 100644
|
||||
--- a/tests/common/protocol/test_hostplugin.py
|
||||
+++ b/tests/common/protocol/test_hostplugin.py
|
||||
@@ -365,8 +365,7 @@ class TestHostPlugin(HttpRequestPredicates, AgentTestCase):
|
||||
# ensure host plugin is not set as default
|
||||
self.assertFalse(wire.HostPluginProtocol.is_default_channel)
|
||||
|
||||
- @patch("azurelinuxagent.common.event.add_event")
|
||||
- def test_put_status_error_reporting(self, patch_add_event):
|
||||
+ def test_put_status_error_reporting(self):
|
||||
"""
|
||||
Validate the telemetry when uploading status fails
|
||||
"""
|
||||
@@ -377,22 +376,22 @@ class TestHostPlugin(HttpRequestPredicates, AgentTestCase):
|
||||
|
||||
put_error = wire.HttpError("put status http error")
|
||||
with patch.object(restutil, "http_put", side_effect=put_error):
|
||||
- with patch.object(wire.HostPluginProtocol,
|
||||
- "ensure_initialized", return_value=True):
|
||||
- self.assertRaises(wire.ProtocolError, wire_protocol_client.upload_status_blob)
|
||||
-
|
||||
- # The agent tries to upload via HostPlugin and that fails due to
|
||||
- # http_put having a side effect of "put_error"
|
||||
- #
|
||||
- # The agent tries to upload using a direct connection, and that succeeds.
|
||||
- self.assertEqual(1, wire_protocol_client.status_blob.upload.call_count) # pylint: disable=no-member
|
||||
- # The agent never touches the default protocol is this code path, so no change.
|
||||
- self.assertFalse(wire.HostPluginProtocol.is_default_channel)
|
||||
- # The agent never logs telemetry event for direct fallback
|
||||
- self.assertEqual(1, patch_add_event.call_count)
|
||||
- self.assertEqual('ReportStatus', patch_add_event.call_args[1]['op'])
|
||||
- self.assertTrue('Falling back to direct' in patch_add_event.call_args[1]['message'])
|
||||
- self.assertEqual(True, patch_add_event.call_args[1]['is_success'])
|
||||
+ with patch.object(wire.HostPluginProtocol, "ensure_initialized", return_value=True):
|
||||
+ with patch("azurelinuxagent.common.event.add_event") as patch_add_event:
|
||||
+ self.assertRaises(wire.ProtocolError, wire_protocol_client.upload_status_blob)
|
||||
+
|
||||
+ # The agent tries to upload via HostPlugin and that fails due to
|
||||
+ # http_put having a side effect of "put_error"
|
||||
+ #
|
||||
+ # The agent tries to upload using a direct connection, and that succeeds.
|
||||
+ self.assertEqual(1, wire_protocol_client.status_blob.upload.call_count) # pylint: disable=no-member
|
||||
+ # The agent never touches the default protocol is this code path, so no change.
|
||||
+ self.assertFalse(wire.HostPluginProtocol.is_default_channel)
|
||||
+ # The agent never logs telemetry event for direct fallback
|
||||
+ self.assertEqual(1, patch_add_event.call_count)
|
||||
+ self.assertEqual('ReportStatus', patch_add_event.call_args[1]['op'])
|
||||
+ self.assertTrue('Falling back to direct' in patch_add_event.call_args[1]['message'])
|
||||
+ self.assertEqual(True, patch_add_event.call_args[1]['is_success'])
|
||||
|
||||
def test_validate_http_request_when_uploading_status(self):
|
||||
"""Validate correct set of data is sent to HostGAPlugin when reporting VM status"""
|
||||
diff --git a/tests/common/protocol/test_wire.py b/tests/common/protocol/test_wire.py
|
||||
index c3dc9461..bec4634f 100644
|
||||
--- a/tests/common/protocol/test_wire.py
|
||||
+++ b/tests/common/protocol/test_wire.py
|
||||
@@ -497,12 +497,6 @@ class TestWireProtocol(AgentTestCase, HttpRequestPredicates):
|
||||
client.report_event(self._get_telemetry_events_generator(event_list), flush=True)
|
||||
self.assertEqual(mock_http_request.call_count, 3)
|
||||
|
||||
- def test_get_header_for_cert_should_use_triple_des(self, *_):
|
||||
- with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
|
||||
- headers = protocol.client.get_header_for_cert()
|
||||
- self.assertIn("x-ms-cipher-name", headers)
|
||||
- self.assertEqual(headers["x-ms-cipher-name"], "DES_EDE3_CBC", "Unexpected x-ms-cipher-name")
|
||||
-
|
||||
def test_get_header_for_remote_access_should_use_aes128(self, *_):
|
||||
with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
|
||||
headers = protocol.client.get_header_for_remote_access()
|
||||
@@ -1096,7 +1090,7 @@ class UpdateGoalStateTestCase(HttpRequestPredicates, AgentTestCase):
|
||||
self.assertEqual(protocol.client.get_hosting_env().deployment_name, new_hosting_env_deployment_name)
|
||||
self.assertEqual(protocol.client.get_shared_conf().xml_text, new_shared_conf)
|
||||
self.assertEqual(sequence_number, new_sequence_number)
|
||||
- self.assertEqual(len(protocol.client.get_certs().cert_list.certificates), 0)
|
||||
+ self.assertEqual(len(protocol.client.get_certs().summary), 0)
|
||||
|
||||
self.assertEqual(protocol.client.get_host_plugin().container_id, new_container_id)
|
||||
self.assertEqual(protocol.client.get_host_plugin().role_config_name, new_role_config_name)
|
||||
diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py
|
||||
index 167e69dc..376e9fc0 100644
|
||||
--- a/tests/ga/test_update.py
|
||||
+++ b/tests/ga/test_update.py
|
||||
@@ -2059,7 +2059,7 @@ class TryUpdateGoalStateTestCase(HttpRequestPredicates, AgentTestCase):
|
||||
# Double check the certificates are correct
|
||||
goal_state = protocol.get_goal_state()
|
||||
|
||||
- thumbprints = [c.thumbprint for c in goal_state.certs.cert_list.certificates]
|
||||
+ thumbprints = [c["thumbprint"] for c in goal_state.certs.summary]
|
||||
|
||||
for extension in goal_state.extensions_goal_state.extensions:
|
||||
for settings in extension.settings:
|
||||
--
|
||||
2.47.3
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
From 93376d3c37882f246c51843ff1b327600f074f81 Mon Sep 17 00:00:00 2001
|
||||
From: Yuxin Sun <yuxisun@redhat.com>
|
||||
Date: Fri, 27 Jun 2025 01:34:20 +0800
|
||||
Subject: [PATCH 1/2] Use systemctl instead of service to manager services in
|
||||
new RHEL versions (#3403)
|
||||
|
||||
RH-Author: yuxisun <None>
|
||||
RH-MergeRequest: 20: Use systemctl instead of service to manager services in new RHEL versions (#3403)
|
||||
RH-Jira: RHEL-109465
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
RH-Commit: [1/1] 95bb66dad7fda08c89a88ca347e55ec18f75d8e8 (yuxisun/WALinuxAgent-src)
|
||||
|
||||
Signed-off-by: Yuxin Sun <yuxisun@redhat.com>
|
||||
(cherry picked from commit a6cfdfdc3e04884a08cd6dd20fa035b687943fe9)
|
||||
---
|
||||
azurelinuxagent/common/osutil/redhat.py | 14 +++++++++++++-
|
||||
1 file changed, 13 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/azurelinuxagent/common/osutil/redhat.py b/azurelinuxagent/common/osutil/redhat.py
|
||||
index b85b2d42..cf2d2f78 100644
|
||||
--- a/azurelinuxagent/common/osutil/redhat.py
|
||||
+++ b/azurelinuxagent/common/osutil/redhat.py
|
||||
@@ -245,6 +245,18 @@ class RedhatOSModernUtil(RedhatOSUtil):
|
||||
def __init__(self): # pylint: disable=W0235
|
||||
super(RedhatOSModernUtil, self).__init__()
|
||||
|
||||
+ def restart_ssh_service(self):
|
||||
+ return shellutil.run("systemctl condrestart sshd", chk_err=False)
|
||||
+
|
||||
+ def stop_agent_service(self):
|
||||
+ return shellutil.run("systemctl stop {0}".format(self.service_name), chk_err=False)
|
||||
+
|
||||
+ def start_agent_service(self):
|
||||
+ return shellutil.run("systemctl start {0}".format(self.service_name), chk_err=False)
|
||||
+
|
||||
+ def restart_network_manager(self):
|
||||
+ shellutil.run("systemctl restart NetworkManager")
|
||||
+
|
||||
def restart_if(self, ifname, retries=3, wait=5):
|
||||
"""
|
||||
Restart an interface by bouncing the link. systemd-networkd observes
|
||||
@@ -270,7 +282,7 @@ class RedhatOSModernUtil(RedhatOSUtil):
|
||||
# RedhatOSUtil was updated to conditionally run NetworkManager restart in response to a race condition between
|
||||
# NetworkManager restart and the agent restarting the network interface during publish_hostname. Keeping the
|
||||
# NetworkManager restart in RedhatOSModernUtil because the issue was not reproduced on these versions.
|
||||
- shellutil.run("service NetworkManager restart")
|
||||
+ self.restart_network_manager()
|
||||
DefaultOSUtil.publish_hostname(self, hostname)
|
||||
|
||||
def set_dhcp_hostname(self, hostname):
|
||||
--
|
||||
2.39.3
|
||||
|
||||
201
wla-docs-add-waagent-manpage-3401.patch
Normal file
201
wla-docs-add-waagent-manpage-3401.patch
Normal file
@ -0,0 +1,201 @@
|
||||
From 079c5ced40fe1a9153af56d86a2094060ee9aa3e Mon Sep 17 00:00:00 2001
|
||||
From: Li Tian <94442129+litian1992@users.noreply.github.com>
|
||||
Date: Tue, 5 Aug 2025 03:18:10 +0800
|
||||
Subject: [PATCH 2/2] docs: add waagent manpage (#3401)
|
||||
|
||||
RH-Author: Li Tian <None>
|
||||
RH-MergeRequest: 21: redhat: docs: add waagent manpage (RHEL-10) (#3401)
|
||||
RH-Jira: RHEL-96792
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/1] 7084e622fbea114a2bf70f5125a40f4ab26415a5 (litian1/WALinuxAgent)
|
||||
|
||||
* docs: add waagent manpage
|
||||
|
||||
Add also man page handler in setup.py
|
||||
|
||||
---------
|
||||
|
||||
Signed-off-by: Li Tian <litian@redhat.com>
|
||||
Co-authored-by: maddieford <93676569+maddieford@users.noreply.github.com>
|
||||
(cherry picked from commit 7f9b5568b6572745b7d0776de0ff6fb70a28dba0)
|
||||
Signed-off-by: Li Tian <litian@redhat.com>
|
||||
---
|
||||
.distro/WALinuxAgent.spec | 1 +
|
||||
doc/man/waagent.1 | 117 ++++++++++++++++++++++++++++++++++++++
|
||||
setup.py | 14 +++++
|
||||
3 files changed, 132 insertions(+)
|
||||
create mode 100644 doc/man/waagent.1
|
||||
|
||||
diff --git a/doc/man/waagent.1 b/doc/man/waagent.1
|
||||
new file mode 100644
|
||||
index 00000000..b1d8e9eb
|
||||
--- /dev/null
|
||||
+++ b/doc/man/waagent.1
|
||||
@@ -0,0 +1,117 @@
|
||||
+.TH WAAGENT 1 "June 2025" "Azure Linux Agent" "System Administration"
|
||||
+.SH NAME
|
||||
+waagent \- Azure Linux VM Agent
|
||||
+.SH SYNOPSIS
|
||||
+.B waagent
|
||||
+[-verbose] [-force] [-help] [\fISUBCOMMAND\fR]...
|
||||
+
|
||||
+.SH DESCRIPTION
|
||||
+The Azure Linux Agent (waagent) manages virtual machine interaction with the Azure fabric controller.
|
||||
+
|
||||
+Most subcommands are not meant to be run directly by the user. However, some subcommands may be useful for debugging (such as collect-logs, version, and show-configuration) and deprovisioning.
|
||||
+
|
||||
+.SH SUBCOMMANDS
|
||||
+.TP
|
||||
+\fB-collect-logs\fR
|
||||
+Runs the log collector utility that collects relevant agent logs for debugging and stores them in the agent folder on disk. Exact location will be shown when run. Use flag \fB-full\fR for more exhaustive log collection.
|
||||
+
|
||||
+.TP
|
||||
+\fB-configuration-path FILE\fR
|
||||
+Used together with \fB-start\fR or \fB-daemon\fR to specify configuration file. Default to /etc/waagent.conf.
|
||||
+
|
||||
+.TP
|
||||
+\fB-daemon -start\fR
|
||||
+Run waagent as a daemon in background.
|
||||
+
|
||||
+.TP
|
||||
+\fB-deprovision\fR
|
||||
+Attempt to clean the system and make it suitable for re-provisioning. WARNING: Deprovision does not guarantee that the image is cleared of all sensitive information and suitable for redistribution.
|
||||
+
|
||||
+.TP
|
||||
+\fB-deprovision+user\fR
|
||||
+Same as \fB-deprovision\fR, but also removes the last provisioned user account.
|
||||
+
|
||||
+.TP
|
||||
+\fB-register-service\fR
|
||||
+Register waagent as a service and enable it.
|
||||
+
|
||||
+.TP
|
||||
+\fB-run-exthandlers\fR
|
||||
+Run check for updates to waagent and extension handler. Note that outputs to /dev/console will be temporarily suspended.
|
||||
+
|
||||
+.TP
|
||||
+\fB-setup-firewall=IP\fR
|
||||
+Set up firewall rules for endpoint \fBIP\fR.
|
||||
+
|
||||
+.TP
|
||||
+\fB-show-configuration\fR
|
||||
+Print the current configuration, including values read from waagent.conf.
|
||||
+
|
||||
+.TP
|
||||
+\fB-help\fR
|
||||
+Display usage information.
|
||||
+
|
||||
+.TP
|
||||
+\fB-version\fR
|
||||
+Show the current version of the agent.
|
||||
+
|
||||
+.SH CONFIGURATION
|
||||
+The agent is configured via this file by default:
|
||||
+
|
||||
+.B /etc/waagent.conf
|
||||
+
|
||||
+This file contains key=value settings that control agent behavior, including provisioning, disk formatting, resource limits, and certificate handling.
|
||||
+
|
||||
+Example entries:
|
||||
+.RS
|
||||
+Provisioning.Enabled=y
|
||||
+ResourceDisk.Format=y
|
||||
+ResourceDisk.MountPoint=/mnt/resource
|
||||
+RSA.KeyLength=2048
|
||||
+Logs.Verbose=y
|
||||
+.RE
|
||||
+
|
||||
+.SH FILES AND DIRECTORIES
|
||||
+.TP
|
||||
+\fB/etc/waagent.conf\fR
|
||||
+Main configuration file.
|
||||
+
|
||||
+.TP
|
||||
+\fB/var/lib/waagent\fR
|
||||
+State files and provisioning artifacts.
|
||||
+
|
||||
+.TP
|
||||
+\fB/var/log/waagent.log\fR
|
||||
+Agent log file.
|
||||
+
|
||||
+.SH SERVICES
|
||||
+On systemd systems, the agent runs as:
|
||||
+.RS
|
||||
+.B systemctl start <waagent.service|walinuxagent.service>
|
||||
+.B systemctl enable <waagent.service|walinuxagent.service>
|
||||
+.RE
|
||||
+
|
||||
+.SH EXIT STATUS
|
||||
+Zero on success, non-zero on error.
|
||||
+
|
||||
+.SH EXAMPLES
|
||||
+.TP
|
||||
+Deprovision before capturing an image:
|
||||
+.RS
|
||||
+waagent -deprovision+user && rm -rf /var/lib/waagent && shutdown -h now
|
||||
+.RE
|
||||
+
|
||||
+.SH SEE ALSO
|
||||
+.BR systemctl (1),
|
||||
+.BR cloud-init (1)
|
||||
+
|
||||
+.SH HOMEPAGE
|
||||
+.B https://github.com/Azure/WALinuxAgent
|
||||
+
|
||||
+.B https://learn.microsoft.com/en-us/azure/virtual-machines/extensions/agent-linux
|
||||
+
|
||||
+.SH COPYRIGHT
|
||||
+Copyright 2018 Microsoft Corporation
|
||||
+
|
||||
+.SH AUTHORS
|
||||
+Microsoft Azure Linux Team
|
||||
diff --git a/setup.py b/setup.py
|
||||
index cf6e90b5..9ed135fb 100755
|
||||
--- a/setup.py
|
||||
+++ b/setup.py
|
||||
@@ -17,7 +17,9 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
+import gzip
|
||||
import os
|
||||
+import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
@@ -87,6 +89,16 @@ def set_udev_files(data_files, dest="/etc/udev/rules.d/", src=None):
|
||||
"config/99-azure-product-uuid.rules"]
|
||||
data_files.append((dest, src))
|
||||
|
||||
+def set_man_files(data_files, dest="/usr/share/man/man1", src=None):
|
||||
+ if src is None:
|
||||
+ src = ["doc/man/waagent.1"]
|
||||
+ src_gz = []
|
||||
+ for file in src:
|
||||
+ with open(file, 'rb') as f_in, gzip.open(file+".gz", 'wb') as f_out:
|
||||
+ shutil.copyfileobj(f_in, f_out)
|
||||
+ src_gz.append(file+".gz")
|
||||
+ data_files.append((dest, src_gz))
|
||||
+
|
||||
|
||||
def get_data_files(name, version, fullname): # pylint: disable=R0912
|
||||
"""
|
||||
@@ -107,6 +119,7 @@ def get_data_files(name, version, fullname): # pylint: disable=R0912
|
||||
set_conf_files(data_files)
|
||||
set_logrotate_files(data_files)
|
||||
set_udev_files(data_files)
|
||||
+ set_man_files(data_files)
|
||||
if version.startswith("8") or version.startswith("9"):
|
||||
# redhat 8+ uses systemd and python3
|
||||
set_systemd_files(data_files, dest=systemd_dir_path,
|
||||
@@ -255,6 +268,7 @@ def get_data_files(name, version, fullname): # pylint: disable=R0912
|
||||
set_logrotate_files(data_files)
|
||||
set_udev_files(data_files)
|
||||
set_systemd_files(data_files, dest=systemd_dir_path)
|
||||
+ set_man_files(data_files)
|
||||
else:
|
||||
# Use default setting
|
||||
set_bin_files(data_files, dest=agent_bin_path)
|
||||
--
|
||||
2.39.3
|
||||
|
||||
@ -1,288 +0,0 @@
|
||||
From 3747dabadea2fe288e6991723e5364179b2906dd Mon Sep 17 00:00:00 2001
|
||||
From: Nageswara Nandigam <84482346+nagworld9@users.noreply.github.com>
|
||||
Date: Mon, 9 Oct 2023 11:14:30 -0700
|
||||
Subject: [PATCH] skip cgorup monitor (#2939)
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 14: skip cgorup monitor (#2939)
|
||||
RH-Jira: RHEL-46713
|
||||
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Commit: [1/1] 613c87b13204159b6b33214d0cf02ed25bcd67e7 (anisinha/centos-wa-linux-agent)
|
||||
|
||||
(cherry picked from commit 5bad0b4b19c907386b80ec18ad1423cdb7f3a050)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
|
||||
Conflicts:
|
||||
azurelinuxagent/agent.py
|
||||
azurelinuxagent/common/logcollector.py
|
||||
tests/common/test_logcollector.py
|
||||
All due to libraries being moved around in upstream.
|
||||
---
|
||||
azurelinuxagent/agent.py | 30 +++++++++++++++++++-------
|
||||
azurelinuxagent/common/logcollector.py | 15 +------------
|
||||
azurelinuxagent/ga/collect_logs.py | 10 ++++-----
|
||||
tests/common/test_logcollector.py | 16 +++++++-------
|
||||
tests/test_agent.py | 8 +++----
|
||||
5 files changed, 40 insertions(+), 39 deletions(-)
|
||||
|
||||
diff --git a/azurelinuxagent/agent.py b/azurelinuxagent/agent.py
|
||||
index 8c303482..0fb681e6 100644
|
||||
--- a/azurelinuxagent/agent.py
|
||||
+++ b/azurelinuxagent/agent.py
|
||||
@@ -30,6 +30,7 @@ import sys
|
||||
import threading
|
||||
from azurelinuxagent.common import cgroupconfigurator, logcollector
|
||||
from azurelinuxagent.common.cgroupapi import SystemdCgroupsApi
|
||||
+from azurelinuxagent.common.cgroup import AGENT_LOG_COLLECTOR, CpuCgroup, MemoryCgroup
|
||||
|
||||
import azurelinuxagent.common.conf as conf
|
||||
import azurelinuxagent.common.event as event
|
||||
@@ -204,11 +205,10 @@ class Agent(object):
|
||||
logger.info("Running log collector mode normal")
|
||||
|
||||
# Check the cgroups unit
|
||||
- cpu_cgroup_path, memory_cgroup_path, log_collector_monitor = None, None, None
|
||||
- if CollectLogsHandler.should_validate_cgroups():
|
||||
- cgroups_api = SystemdCgroupsApi()
|
||||
- cpu_cgroup_path, memory_cgroup_path = cgroups_api.get_process_cgroup_paths("self")
|
||||
-
|
||||
+ log_collector_monitor = None
|
||||
+ cgroups_api = SystemdCgroupsApi()
|
||||
+ cpu_cgroup_path, memory_cgroup_path = cgroups_api.get_process_cgroup_paths("self")
|
||||
+ if CollectLogsHandler.is_enabled_monitor_cgroups_check():
|
||||
cpu_slice_matches = (cgroupconfigurator.LOGCOLLECTOR_SLICE in cpu_cgroup_path)
|
||||
memory_slice_matches = (cgroupconfigurator.LOGCOLLECTOR_SLICE in memory_cgroup_path)
|
||||
|
||||
@@ -221,10 +221,24 @@ class Agent(object):
|
||||
|
||||
sys.exit(logcollector.INVALID_CGROUPS_ERRCODE)
|
||||
|
||||
+ def initialize_cgroups_tracking(cpu_cgroup_path, memory_cgroup_path):
|
||||
+ cpu_cgroup = CpuCgroup(AGENT_LOG_COLLECTOR, cpu_cgroup_path)
|
||||
+ msg = "Started tracking cpu cgroup {0}".format(cpu_cgroup)
|
||||
+ logger.info(msg)
|
||||
+ cpu_cgroup.initialize_cpu_usage()
|
||||
+ memory_cgroup = MemoryCgroup(AGENT_LOG_COLLECTOR, memory_cgroup_path)
|
||||
+ msg = "Started tracking memory cgroup {0}".format(memory_cgroup)
|
||||
+ logger.info(msg)
|
||||
+ return [cpu_cgroup, memory_cgroup]
|
||||
+
|
||||
try:
|
||||
- log_collector = LogCollector(is_full_mode, cpu_cgroup_path, memory_cgroup_path)
|
||||
- log_collector_monitor = get_log_collector_monitor_handler(log_collector.cgroups)
|
||||
- log_collector_monitor.run()
|
||||
+ log_collector = LogCollector(is_full_mode)
|
||||
+ # Running log collector resource(CPU, Memory) monitoring only if agent starts the log collector.
|
||||
+ # If Log collector start by any other means, then it will not be monitored.
|
||||
+ if CollectLogsHandler.is_enabled_monitor_cgroups_check():
|
||||
+ tracked_cgroups = initialize_cgroups_tracking(cpu_cgroup_path, memory_cgroup_path)
|
||||
+ log_collector_monitor = get_log_collector_monitor_handler(tracked_cgroups)
|
||||
+ log_collector_monitor.run()
|
||||
archive = log_collector.collect_logs_and_get_archive()
|
||||
logger.info("Log collection successfully completed. Archive can be found at {0} "
|
||||
"and detailed log output can be found at {1}".format(archive, OUTPUT_RESULTS_FILE_PATH))
|
||||
diff --git a/azurelinuxagent/common/logcollector.py b/azurelinuxagent/common/logcollector.py
|
||||
index fe62a7db..5f45a7de 100644
|
||||
--- a/azurelinuxagent/common/logcollector.py
|
||||
+++ b/azurelinuxagent/common/logcollector.py
|
||||
@@ -26,7 +26,6 @@ import zipfile
|
||||
from datetime import datetime
|
||||
from heapq import heappush, heappop
|
||||
|
||||
-from azurelinuxagent.common.cgroup import CpuCgroup, AGENT_LOG_COLLECTOR, MemoryCgroup
|
||||
from azurelinuxagent.common.conf import get_lib_dir, get_ext_log_dir, get_agent_log_file
|
||||
from azurelinuxagent.common.event import initialize_event_logger_vminfo_common_parameters
|
||||
from azurelinuxagent.common.future import ustr
|
||||
@@ -71,14 +70,13 @@ class LogCollector(object):
|
||||
|
||||
_TRUNCATED_FILE_PREFIX = "truncated_"
|
||||
|
||||
- def __init__(self, is_full_mode=False, cpu_cgroup_path=None, memory_cgroup_path=None):
|
||||
+ def __init__(self, is_full_mode=False):
|
||||
self._is_full_mode = is_full_mode
|
||||
self._manifest = MANIFEST_FULL if is_full_mode else MANIFEST_NORMAL
|
||||
self._must_collect_files = self._expand_must_collect_files()
|
||||
self._create_base_dirs()
|
||||
self._set_logger()
|
||||
self._initialize_telemetry()
|
||||
- self.cgroups = self._set_resource_usage_cgroups(cpu_cgroup_path, memory_cgroup_path)
|
||||
|
||||
@staticmethod
|
||||
def _mkdir(dirname):
|
||||
@@ -105,17 +103,6 @@ class LogCollector(object):
|
||||
_LOGGER.addHandler(_f_handler)
|
||||
_LOGGER.setLevel(logging.INFO)
|
||||
|
||||
- @staticmethod
|
||||
- def _set_resource_usage_cgroups(cpu_cgroup_path, memory_cgroup_path):
|
||||
- cpu_cgroup = CpuCgroup(AGENT_LOG_COLLECTOR, cpu_cgroup_path)
|
||||
- msg = "Started tracking cpu cgroup {0}".format(cpu_cgroup)
|
||||
- _LOGGER.info(msg)
|
||||
- cpu_cgroup.initialize_cpu_usage()
|
||||
- memory_cgroup = MemoryCgroup(AGENT_LOG_COLLECTOR, memory_cgroup_path)
|
||||
- msg = "Started tracking memory cgroup {0}".format(memory_cgroup)
|
||||
- _LOGGER.info(msg)
|
||||
- return [cpu_cgroup, memory_cgroup]
|
||||
-
|
||||
@staticmethod
|
||||
def _initialize_telemetry():
|
||||
protocol = get_protocol_util().get_protocol(init_goal_state=False)
|
||||
diff --git a/azurelinuxagent/ga/collect_logs.py b/azurelinuxagent/ga/collect_logs.py
|
||||
index 95c42f3a..4f42e149 100644
|
||||
--- a/azurelinuxagent/ga/collect_logs.py
|
||||
+++ b/azurelinuxagent/ga/collect_logs.py
|
||||
@@ -83,16 +83,16 @@ class CollectLogsHandler(ThreadHandlerInterface):
|
||||
return CollectLogsHandler._THREAD_NAME
|
||||
|
||||
@staticmethod
|
||||
- def enable_cgroups_validation():
|
||||
+ def enable_monitor_cgroups_check():
|
||||
os.environ[CollectLogsHandler.__CGROUPS_FLAG_ENV_VARIABLE] = "1"
|
||||
|
||||
@staticmethod
|
||||
- def disable_cgroups_validation():
|
||||
+ def disable_monitor_cgroups_check():
|
||||
if CollectLogsHandler.__CGROUPS_FLAG_ENV_VARIABLE in os.environ:
|
||||
del os.environ[CollectLogsHandler.__CGROUPS_FLAG_ENV_VARIABLE]
|
||||
|
||||
@staticmethod
|
||||
- def should_validate_cgroups():
|
||||
+ def is_enabled_monitor_cgroups_check():
|
||||
if CollectLogsHandler.__CGROUPS_FLAG_ENV_VARIABLE in os.environ:
|
||||
return os.environ[CollectLogsHandler.__CGROUPS_FLAG_ENV_VARIABLE] == "1"
|
||||
return False
|
||||
@@ -147,7 +147,7 @@ class CollectLogsHandler(ThreadHandlerInterface):
|
||||
time.sleep(_INITIAL_LOG_COLLECTION_DELAY)
|
||||
|
||||
try:
|
||||
- CollectLogsHandler.enable_cgroups_validation()
|
||||
+ CollectLogsHandler.enable_monitor_cgroups_check()
|
||||
if self.protocol_util is None or self.protocol is None:
|
||||
self.init_protocols()
|
||||
|
||||
@@ -162,7 +162,7 @@ class CollectLogsHandler(ThreadHandlerInterface):
|
||||
except Exception as e:
|
||||
logger.error("An error occurred in the log collection thread; will exit the thread.\n{0}", ustr(e))
|
||||
finally:
|
||||
- CollectLogsHandler.disable_cgroups_validation()
|
||||
+ CollectLogsHandler.disable_monitor_cgroups_check()
|
||||
|
||||
def collect_and_send_logs(self):
|
||||
if self._collect_logs():
|
||||
diff --git a/tests/common/test_logcollector.py b/tests/common/test_logcollector.py
|
||||
index 521e0f23..bf402cc7 100644
|
||||
--- a/tests/common/test_logcollector.py
|
||||
+++ b/tests/common/test_logcollector.py
|
||||
@@ -212,7 +212,7 @@ diskinfo,""".format(folder_to_list, file_to_collect)
|
||||
|
||||
with patch("azurelinuxagent.common.logcollector.MANIFEST_NORMAL", manifest):
|
||||
with patch('azurelinuxagent.common.logcollector.LogCollector._initialize_telemetry'):
|
||||
- log_collector = LogCollector(cpu_cgroup_path="dummy_cpu_path", memory_cgroup_path="dummy_memory_path")
|
||||
+ log_collector = LogCollector()
|
||||
archive = log_collector.collect_logs_and_get_archive()
|
||||
|
||||
with open(self.output_results_file_path, "r") as fh:
|
||||
@@ -241,7 +241,7 @@ copy,{0}
|
||||
|
||||
with patch("azurelinuxagent.common.logcollector.MANIFEST_FULL", manifest):
|
||||
with patch('azurelinuxagent.common.logcollector.LogCollector._initialize_telemetry'):
|
||||
- log_collector = LogCollector(is_full_mode=True, cpu_cgroup_path="dummy_cpu_path", memory_cgroup_path="dummy_memory_path")
|
||||
+ log_collector = LogCollector(is_full_mode=True)
|
||||
archive = log_collector.collect_logs_and_get_archive()
|
||||
|
||||
self._assert_archive_created(archive)
|
||||
@@ -255,7 +255,7 @@ copy,{0}
|
||||
# and combined they do not cross the archive size threshold.
|
||||
|
||||
with patch('azurelinuxagent.common.logcollector.LogCollector._initialize_telemetry'):
|
||||
- log_collector = LogCollector(cpu_cgroup_path="dummy_cpu_path", memory_cgroup_path="dummy_memory_path")
|
||||
+ log_collector = LogCollector()
|
||||
archive = log_collector.collect_logs_and_get_archive()
|
||||
|
||||
self._assert_archive_created(archive)
|
||||
@@ -277,7 +277,7 @@ copy,{0}
|
||||
# Set the size limit so that some files are too large to collect in full.
|
||||
with patch("azurelinuxagent.common.logcollector._FILE_SIZE_LIMIT", SMALL_FILE_SIZE):
|
||||
with patch('azurelinuxagent.common.logcollector.LogCollector._initialize_telemetry'):
|
||||
- log_collector = LogCollector(cpu_cgroup_path="dummy_cpu_path", memory_cgroup_path="dummy_memory_path")
|
||||
+ log_collector = LogCollector()
|
||||
archive = log_collector.collect_logs_and_get_archive()
|
||||
|
||||
self._assert_archive_created(archive)
|
||||
@@ -311,7 +311,7 @@ copy,{0}
|
||||
with patch("azurelinuxagent.common.logcollector._UNCOMPRESSED_ARCHIVE_SIZE_LIMIT", 10 * 1024 * 1024):
|
||||
with patch("azurelinuxagent.common.logcollector._MUST_COLLECT_FILES", must_collect_files):
|
||||
with patch('azurelinuxagent.common.logcollector.LogCollector._initialize_telemetry'):
|
||||
- log_collector = LogCollector(cpu_cgroup_path="dummy_cpu_path", memory_cgroup_path="dummy_memory_path")
|
||||
+ log_collector = LogCollector()
|
||||
archive = log_collector.collect_logs_and_get_archive()
|
||||
|
||||
self._assert_archive_created(archive)
|
||||
@@ -362,7 +362,7 @@ copy,{0}
|
||||
# Ensure the archive reflects the state of files on the disk at collection time. If a file was updated, it
|
||||
# needs to be updated in the archive, deleted if removed from disk, and added if not previously seen.
|
||||
with patch('azurelinuxagent.common.logcollector.LogCollector._initialize_telemetry'):
|
||||
- log_collector = LogCollector(cpu_cgroup_path="dummy_cpu_path", memory_cgroup_path="dummy_memory_path")
|
||||
+ log_collector = LogCollector()
|
||||
first_archive = log_collector.collect_logs_and_get_archive()
|
||||
self._assert_archive_created(first_archive)
|
||||
|
||||
@@ -433,7 +433,7 @@ copy,{0}
|
||||
with patch("azurelinuxagent.common.logcollector._MUST_COLLECT_FILES", must_collect_files):
|
||||
with patch("azurelinuxagent.common.logcollector._FILE_SIZE_LIMIT", SMALL_FILE_SIZE):
|
||||
with patch('azurelinuxagent.common.logcollector.LogCollector._initialize_telemetry'):
|
||||
- log_collector = LogCollector(cpu_cgroup_path="dummy_cpu_path", memory_cgroup_path="dummy_memory_path")
|
||||
+ log_collector = LogCollector()
|
||||
archive = log_collector.collect_logs_and_get_archive()
|
||||
|
||||
self._assert_archive_created(archive)
|
||||
@@ -455,7 +455,7 @@ copy,{0}
|
||||
with patch("azurelinuxagent.common.logcollector._MUST_COLLECT_FILES", must_collect_files):
|
||||
with patch("azurelinuxagent.common.logcollector._FILE_SIZE_LIMIT", SMALL_FILE_SIZE):
|
||||
with patch('azurelinuxagent.common.logcollector.LogCollector._initialize_telemetry'):
|
||||
- log_collector = LogCollector(cpu_cgroup_path="dummy_cpu_path", memory_cgroup_path="dummy_memory_path")
|
||||
+ log_collector = LogCollector()
|
||||
second_archive = log_collector.collect_logs_and_get_archive()
|
||||
|
||||
expected_files = [
|
||||
diff --git a/tests/test_agent.py b/tests/test_agent.py
|
||||
index f0f773f0..f5e87c87 100644
|
||||
--- a/tests/test_agent.py
|
||||
+++ b/tests/test_agent.py
|
||||
@@ -231,7 +231,7 @@ class TestAgent(AgentTestCase):
|
||||
@patch("azurelinuxagent.agent.LogCollector")
|
||||
def test_calls_collect_logs_on_valid_cgroups(self, mock_log_collector):
|
||||
try:
|
||||
- CollectLogsHandler.enable_cgroups_validation()
|
||||
+ CollectLogsHandler.enable_monitor_cgroups_check()
|
||||
mock_log_collector.run = Mock()
|
||||
|
||||
def mock_cgroup_paths(*args, **kwargs):
|
||||
@@ -246,12 +246,12 @@ class TestAgent(AgentTestCase):
|
||||
|
||||
mock_log_collector.assert_called_once()
|
||||
finally:
|
||||
- CollectLogsHandler.disable_cgroups_validation()
|
||||
+ CollectLogsHandler.disable_monitor_cgroups_check()
|
||||
|
||||
@patch("azurelinuxagent.agent.LogCollector")
|
||||
def test_doesnt_call_collect_logs_on_invalid_cgroups(self, mock_log_collector):
|
||||
try:
|
||||
- CollectLogsHandler.enable_cgroups_validation()
|
||||
+ CollectLogsHandler.enable_monitor_cgroups_check()
|
||||
mock_log_collector.run = Mock()
|
||||
|
||||
def mock_cgroup_paths(*args, **kwargs):
|
||||
@@ -270,7 +270,7 @@ class TestAgent(AgentTestCase):
|
||||
mock_exit.assert_called_once_with(logcollector.INVALID_CGROUPS_ERRCODE)
|
||||
self.assertEqual(exit_error, re)
|
||||
finally:
|
||||
- CollectLogsHandler.disable_cgroups_validation()
|
||||
+ CollectLogsHandler.disable_monitor_cgroups_check()
|
||||
|
||||
def test_it_should_parse_setup_firewall_properly(self):
|
||||
|
||||
--
|
||||
2.39.3
|
||||
|
||||
Loading…
Reference in New Issue
Block a user