-
Notifications
You must be signed in to change notification settings - Fork 268
Expand file tree
/
Copy pathagent.py
More file actions
2343 lines (1966 loc) · 104 KB
/
agent.py
File metadata and controls
2343 lines (1966 loc) · 104 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python
#
# AzureMonitoringLinuxAgent Extension
#
# Copyright 2021 Microsoft Corporation
#
# 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.
from __future__ import print_function
import sys
import os
import os.path
import datetime
import signal
import pwd
import grp
import re
import filecmp
import stat
import traceback
import time
import platform
import subprocess
import json
import base64
import inspect
import shutil
import re
import hashlib
import fileinput
import contextlib
import distro
from collections import OrderedDict
from hashlib import sha256
from shutil import copyfile
from shutil import copytree
from shutil import rmtree
from threading import Thread
import telegraf_utils.telegraf_config_handler as telhandler
import metrics_ext_utils.metrics_constants as metrics_constants
import metrics_ext_utils.metrics_ext_handler as me_handler
import metrics_ext_utils.metrics_common_utils as metrics_utils
if sys.version_info[0] == 3:
import urllib.request as urllib
from urllib.parse import urlparse
import urllib.error as urlerror
elif sys.version_info[0] == 2:
import urllib2 as urllib
from urlparse import urlparse
import urllib2 as urlerror
try:
from Utils.WAAgentUtil import waagent
import Utils.HandlerUtil as HUtil
except Exception as e:
# These utils have checks around the use of them; this is not an exit case
print('Importing utils failed with error: {0}'.format(e))
# This code is taken from the omsagent's extension wrapper.
# This same monkey patch fix is relevant for AMA extension as well.
# This monkey patch duplicates the one made in the waagent import above.
# It is necessary because on 2.6, the waagent monkey patch appears to be overridden
# by the python-future subprocess.check_output backport.
if sys.version_info < (2,7):
def check_output(*popenargs, **kwargs):
r"""Backport from subprocess module from python 2.7"""
if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.')
process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
raise subprocess.CalledProcessError(retcode, cmd, output=output)
return output
# Exception classes used by this module.
class CalledProcessError(Exception):
def __init__(self, returncode, cmd, output=None):
self.returncode = returncode
self.cmd = cmd
self.output = output
def __str__(self):
return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
subprocess.check_output = check_output
subprocess.CalledProcessError = CalledProcessError
# Global Variables
PackagesDirectory = 'packages'
# The BundleFileName values will be replaced by actual values in the release pipeline. See apply_version.sh.
BundleFileNameDeb = 'azuremonitoragent.deb'
BundleFileNameRpm = 'azuremonitoragent.rpm'
BundleFileName = ''
TelegrafBinName = 'telegraf'
InitialRetrySleepSeconds = 30
PackageManager = ''
PackageManagerOptions = ''
MdsdCounterJsonPath = '/etc/opt/microsoft/azuremonitoragent/config-cache/metricCounters.json'
FluentCfgPath = '/etc/opt/microsoft/azuremonitoragent/config-cache/fluentbit/td-agent.conf'
AMASyslogConfigMarkerPath = '/etc/opt/microsoft/azuremonitoragent/config-cache/syslog.marker'
AMASyslogPortFilePath = '/etc/opt/microsoft/azuremonitoragent/config-cache/syslog.port'
AMAFluentPortFilePath = '/etc/opt/microsoft/azuremonitoragent/config-cache/fluent.port'
PreviewFeaturesDirectory = '/etc/opt/microsoft/azuremonitoragent/config-cache/previewFeatures/'
ArcSettingsFile = '/var/opt/azcmagent/localconfig.json'
SupportedArch = set(['x86_64', 'aarch64'])
# Error codes
GenericErrorCode = 1
UnsupportedOperatingSystem = 51
IndeterminateOperatingSystem = 51
MissingorInvalidParameterErrorCode = 53
DPKGOrRPMLockedErrorCode = 56
MissingDependency = 52
# Settings
GenevaConfigKey = "genevaConfiguration"
AzureMonitorConfigKey = "azureMonitorConfiguration"
# Configuration
HUtilObject = None
SettingsSequenceNumber = None
HandlerEnvironment = None
SettingsDict = None
def main():
"""
Main method
Parse out operation from argument, invoke the operation, and finish.
"""
init_waagent_logger()
waagent_log_info('Azure Monitoring Agent for Linux started to handle.')
# Determine the operation being executed
operation = None
try:
option = sys.argv[1]
if re.match('^([-/]*)(disable)', option):
operation = 'Disable'
elif re.match('^([-/]*)(uninstall)', option):
operation = 'Uninstall'
elif re.match('^([-/]*)(install)', option):
operation = 'Install'
elif re.match('^([-/]*)(enable)', option):
operation = 'Enable'
elif re.match('^([-/]*)(update)', option):
operation = 'Update'
elif re.match('^([-/]*)(metrics)', option):
operation = 'Metrics'
elif re.match('^([-/]*)(syslogconfig)', option):
operation = 'Syslogconfig'
except Exception as e:
waagent_log_error(str(e))
if operation is None:
log_and_exit('Unknown', GenericErrorCode, 'No valid operation provided')
# Set up for exit code and any error messages
exit_code = 0
message = '{0} succeeded'.format(operation)
# Avoid entering broken state where manual purge actions are necessary in low disk space scenario
destructive_operations = ['Disable', 'Uninstall']
if operation not in destructive_operations:
exit_code = check_disk_space_availability()
if exit_code != 0:
message = '{0} failed due to low disk space'.format(operation)
log_and_exit(operation, exit_code, message)
# Invoke operation
try:
global HUtilObject
HUtilObject = parse_context(operation)
exit_code, output = operations[operation]()
# Exit code 1 indicates a general problem that doesn't have a more
# specific error code; it often indicates a missing dependency
if exit_code == 1 and operation == 'Install':
message = 'Install failed with exit code 1. For error details, check logs ' \
'in /var/log/azure/Microsoft.Azure.Monitor' \
'.AzureMonitorLinuxAgent'
elif exit_code is DPKGOrRPMLockedErrorCode and operation == 'Install':
message = 'Install failed with exit code {0} because the ' \
'package manager on the VM is currently locked: ' \
'please wait and try again'.format(DPKGOrRPMLockedErrorCode)
elif exit_code != 0:
message = '{0} failed with exit code {1} {2}'.format(operation,
exit_code, output)
except AzureMonitorAgentForLinuxException as e:
exit_code = e.error_code
message = e.get_error_message(operation)
except Exception as e:
exit_code = GenericErrorCode
message = '{0} failed with error: {1}\n' \
'Stacktrace: {2}'.format(operation, e,
traceback.format_exc())
# Finish up and log messages
log_and_exit(operation, exit_code, message)
def check_disk_space_availability():
"""
Check if there is the required space on the machine.
"""
try:
if get_free_space_mb("/var") < 500 or get_free_space_mb("/etc") < 500 or get_free_space_mb("/opt") < 500 :
# 52 is the exit code for missing dependency i.e. disk space
# https://github.com/Azure/azure-marketplace/wiki/Extension-Build-Notes-Best-Practices#error-codes-and-messages-output-to-stderr
return MissingDependency
else:
return 0
except:
print('Failed to check disk usage.')
return 0
def get_free_space_mb(dirname):
"""
Get the free space in MB in the directory path.
"""
st = os.statvfs(dirname)
return (st.f_bavail * st.f_frsize) // (1024 * 1024)
def is_systemd():
"""
Check if the system is using systemd
"""
return os.path.isdir("/run/systemd/system")
def get_service_command(service, *operations):
"""
Get the appropriate service command [sequence] for the provided service name and operation(s)
"""
if is_systemd():
return " && ".join(["systemctl {0} {1}".format(operation, service) for operation in operations])
else:
hutil_log_info("The VM doesn't have systemctl. Using the init.d service to start {0}.".format(service))
return '/etc/init.d/{0} {1}'.format(service, operations[0])
def check_kill_process(pstring):
for line in os.popen("ps ax | grep " + pstring + " | grep -v grep"):
fields = line.split()
pid = fields[0]
os.kill(int(pid), signal.SIGKILL)
def compare_and_copy_bin(src, dest):
# Check if previous file exist at the location, compare the two binaries,
# If the files are not same, remove the older file, and copy the new one
# If they are the same, then we ignore it and don't copy
if os.path.isfile(src ):
if os.path.isfile(dest):
if not filecmp.cmp(src, dest):
# Removing the file in case it is already being run in a process,
# in which case we can get an error "text file busy" while copying
os.remove(dest)
copyfile(src, dest)
else:
# No previous binary exist, simply copy it and make it executable
copyfile(src, dest)
os.chmod(dest, stat.S_IXGRP | stat.S_IRGRP | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IXOTH | stat.S_IROTH)
def copy_amacoreagent_binaries():
amacoreagent_bin_local_path = os.getcwd() + "/amaCoreAgentBin/amacoreagent"
amacoreagent_bin = "/opt/microsoft/azuremonitoragent/bin/amacoreagent"
compare_and_copy_bin(amacoreagent_bin_local_path, amacoreagent_bin)
liblz4x64_bin_local_path = os.getcwd() + "/amaCoreAgentBin/liblz4x64.so"
liblz4x64_bin = "/opt/microsoft/azuremonitoragent/bin/liblz4x64.so"
compare_and_copy_bin(liblz4x64_bin_local_path, liblz4x64_bin)
libgrpc_bin_local_path = os.getcwd() + "/amaCoreAgentBin/libgrpc_csharp_ext.x64.so"
libgrpc_bin = "/opt/microsoft/azuremonitoragent/bin/libgrpc_csharp_ext.x64.so"
compare_and_copy_bin(libgrpc_bin_local_path, libgrpc_bin)
agentlauncher_bin_local_path = os.getcwd() + "/agentLauncherBin/agentlauncher"
agentlauncher_bin = "/opt/microsoft/azuremonitoragent/bin/agentlauncher"
compare_and_copy_bin(agentlauncher_bin_local_path, agentlauncher_bin)
def copy_mdsd_fluentbit_binaries():
current_arch = platform.machine()
mdsd_bin_local_path = os.getcwd() + "/mdsdBin/mdsd_" + current_arch
mdsdmgr_bin_local_path = os.getcwd() + "/mdsdBin/mdsdmgr_" + current_arch
fluentbit_bin_local_path = os.getcwd() + "/fluentBitBin/fluent-bit_" + current_arch
mdsd_bin = "/opt/microsoft/azuremonitoragent/bin/mdsd"
mdsdmgr_bin = "/opt/microsoft/azuremonitoragent/bin/mdsdmgr"
fluentbit_bin = "/opt/microsoft/azuremonitoragent/bin/fluent-bit"
# copy the required libs to our test directory first
copytree("/opt/microsoft/azuremonitoragent/lib", os.getcwd() + "/lib")
canUseSharedmdsd, _ = run_command_and_log('ldd ' + mdsd_bin_local_path + ' | grep "not found"')
canUseSharedmdsdmgr, _ = run_command_and_log('ldd ' + mdsdmgr_bin_local_path + ' | grep "not found"')
if canUseSharedmdsd != 0 and canUseSharedmdsdmgr != 0:
compare_and_copy_bin(mdsd_bin_local_path, mdsd_bin)
compare_and_copy_bin(mdsdmgr_bin_local_path, mdsdmgr_bin)
canUseSharedfluentbit, _ = run_command_and_log('ldd ' + fluentbit_bin_local_path + ' | grep "not found"')
if canUseSharedfluentbit != 0:
compare_and_copy_bin(fluentbit_bin_local_path, fluentbit_bin)
rmtree(os.getcwd() + "/lib")
def install():
"""
Ensure that this VM distro and version are supported.
Install the Azure Monitor Linux Agent package, using retries.
Note: install operation times out from WAAgent at 15 minutes, so do not
wait longer.
"""
exit_if_vm_not_supported('Install')
find_package_manager("Install")
set_os_arch('Install')
vm_dist, vm_ver = find_vm_distro('Install')
# Check if SUSE 15 VMs have /sbin/insserv package (required for AMA 1.14.4+)
if (vm_dist.startswith('suse') or vm_dist.startswith('sles') or vm_dist.startswith('opensuse')) and vm_ver.startswith('15'):
check_insserv, _ = run_command_and_log("which insserv")
if check_insserv != 0:
hutil_log_info("'insserv-compat' package missing from SUSE 15 machine, installing to allow AMA to run.")
insserv_exit_code, insserv_output = run_command_and_log("zypper --non-interactive install insserv-compat")
if insserv_exit_code != 0:
return insserv_exit_code, insserv_output
# Check if Debian 12 VMs have rsyslog package (required for AMA 1.31+)
if (vm_dist.startswith('debian')) and vm_ver.startswith('12'):
check_rsyslog, _ = run_command_and_log("dpkg -s rsyslog")
if check_rsyslog != 0:
hutil_log_info("'rsyslog' package missing from Debian 12 machine, installing to allow AMA to run.")
rsyslog_exit_code, rsyslog_output = run_command_and_log("DEBIAN_FRONTEND=noninteractive apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y rsyslog")
if rsyslog_exit_code != 0:
return rsyslog_exit_code, rsyslog_output
# Check if Amazon 2023 VMs have rsyslog package (required for AMA 1.31+)
if (vm_dist.startswith('amzn')) and vm_ver.startswith('2023'):
check_rsyslog, _ = run_command_and_log("dnf list installed | grep rsyslog.x86_64")
if check_rsyslog != 0:
hutil_log_info("'rsyslog' package missing from Amazon Linux 2023 machine, installing to allow AMA to run.")
rsyslog_exit_code, rsyslog_output = run_command_and_log("dnf install -y rsyslog")
if rsyslog_exit_code != 0:
return rsyslog_exit_code, rsyslog_output
package_directory = os.path.join(os.getcwd(), PackagesDirectory)
bundle_path = os.path.join(package_directory, BundleFileName)
os.chmod(bundle_path, 100)
print(PackageManager, " and ", BundleFileName)
AMAInstallCommand = "{0} {1} -i {2}".format(PackageManager, PackageManagerOptions, bundle_path)
hutil_log_info('Running command "{0}"'.format(AMAInstallCommand))
# Retry, since install can fail due to concurrent package operations
exit_code, output = run_command_with_retries_output(AMAInstallCommand, retries = 15,
retry_check = retry_if_dpkg_or_rpm_locked,
final_check = final_check_if_dpkg_or_rpm_locked)
# Retry install for aarch64 rhel8 VMs as initial install fails to create symlink to /etc/systemd/system/azuremonitoragent.service
# in /etc/systemd/system/multi-user.target.wants/azuremonitoragent.service
if vm_dist.replace(' ','').lower().startswith('redhat') and vm_ver == '8.6' and platform.machine() == 'aarch64':
exit_code, output = run_command_with_retries_output(AMAInstallCommand, retries = 15,
retry_check = retry_if_dpkg_or_rpm_locked,
final_check = final_check_if_dpkg_or_rpm_locked)
if exit_code != 0:
return exit_code, output
# Copy the AMACoreAgent and agentlauncher binaries
# TBD: this method needs to be revisited for aarch64
copy_amacoreagent_binaries()
# Copy Kqle xtension binaries
# Needs to be revisited for aarch64
copy_kqlextension_binaries()
# Copy mdsd and fluent-bit with OpenSSL dynamically linked
if is_feature_enabled('useDynamicSSL'):
# Check if they have libssl.so.1.1 since AMA is built against this version
libssl1_1, _ = run_command_and_log('ldconfig -p | grep libssl.so.1.1')
if libssl1_1 == 0:
copy_mdsd_fluentbit_binaries()
# Comment out the following check in AMA 1.31 as the coreagent & agentlauncher services are not installed with the aarch64 deb/rpm packages.
#
# # CL is diabled in arm64 until we have arm64 binaries from pipelineAgent
# if is_systemd() and platform.machine() == 'aarch64':
# exit_code, output = run_command_and_log('systemctl stop azuremonitor-coreagent && systemctl disable azuremonitor-coreagent')
# exit_code, output = run_command_and_log('systemctl stop azuremonitor-agentlauncher && systemctl disable azuremonitor-agentlauncher')
# Set task limits to max of 65K in suse 12
# Based on Task 9764411: AMA broken after 1.7 in sles 12 - https://dev.azure.com/msazure/One/_workitems/edit/9764411
if exit_code == 0:
vm_dist, _ = find_vm_distro('Install')
if (vm_dist.startswith('suse') or vm_dist.startswith('sles')):
try:
suse_exit_code, suse_output = run_command_and_log("mkdir -p /etc/systemd/system/azuremonitoragent.service.d")
if suse_exit_code != 0:
return suse_exit_code, suse_output
suse_exit_code, suse_output = run_command_and_log("echo '[Service]' > /etc/systemd/system/azuremonitoragent.service.d/override.conf")
if suse_exit_code != 0:
return suse_exit_code, suse_output
suse_exit_code, suse_output = run_command_and_log("echo 'TasksMax=65535' >> /etc/systemd/system/azuremonitoragent.service.d/override.conf")
if suse_exit_code != 0:
return suse_exit_code, suse_output
suse_exit_code, suse_output = run_command_and_log("systemctl daemon-reload")
if suse_exit_code != 0:
return suse_exit_code, suse_output
except:
log_and_exit("install", MissingorInvalidParameterErrorCode, "Failed to update /etc/systemd/system/azuremonitoragent.service.d for suse 12,15" )
return exit_code, output
def uninstall():
"""
Uninstall the Azure Monitor Linux Agent.
This is a somewhat soft uninstall. It is not a purge.
Note: uninstall operation times out from WAAgent at 5 minutes
"""
exit_if_vm_not_supported('Uninstall')
find_package_manager("Uninstall")
if PackageManager == "dpkg":
AMAUninstallCommand = "dpkg -P azuremonitoragent"
elif PackageManager == "rpm":
AMAUninstallCommand = "rpm -e azuremonitoragent"
else:
log_and_exit("Uninstall", UnsupportedOperatingSystem, "The OS has neither rpm nor dpkg" )
hutil_log_info('Running command "{0}"'.format(AMAUninstallCommand))
remove_localsyslog_configs()
# Retry, since uninstall can fail due to concurrent package operations
try:
exit_code, output = run_command_with_retries_output(AMAUninstallCommand, retries = 4,
retry_check = retry_if_dpkg_or_rpm_locked,
final_check = final_check_if_dpkg_or_rpm_locked)
except Exception as ex:
exit_code = GenericErrorCode
output = 'Uninstall failed with error: {0}\n' \
'Stacktrace: {1}'.format(ex, traceback.format_exc())
return exit_code, output
def enable():
"""
Start the Azure Monitor Linux Agent Service
This call will return non-zero or throw an exception if
the settings provided are incomplete or incorrect.
Note: enable operation times out from WAAgent at 5 minutes
"""
public_settings, protected_settings = get_settings()
exit_if_vm_not_supported('Enable')
ensure = OrderedDict([
("azuremonitoragent", False),
("azuremonitoragentmgr", False)
])
# Set traceFlags in publicSettings to enable mdsd tracing. For example, the EventIngest flag can be enabled via "traceFlags": "0x2"
flags = ""
if public_settings is not None and "traceFlags" in public_settings:
flags = "-T {} ".format(public_settings.get("traceFlags"))
# Use an Ordered Dictionary to ensure MDSD_OPTIONS (and other dependent variables) are written after their dependencies
default_configs = OrderedDict([
("MDSD_CONFIG_DIR", "/etc/opt/microsoft/azuremonitoragent"),
("MDSD_LOG_DIR", "/var/opt/microsoft/azuremonitoragent/log"),
("MDSD_ROLE_PREFIX", "/run/azuremonitoragent/default"),
("MDSD_SPOOL_DIRECTORY", "/var/opt/microsoft/azuremonitoragent"),
("MDSD_OPTIONS", "\"{}-A -R -c /etc/opt/microsoft/azuremonitoragent/mdsd.xml -d -r $MDSD_ROLE_PREFIX -S $MDSD_SPOOL_DIRECTORY/eh -L $MDSD_SPOOL_DIRECTORY/events\"".format(flags)),
("MDSD_USE_LOCAL_PERSISTENCY", "true"),
("MDSD_TCMALLOC_RELEASE_FREQ_SEC", "1"),
("MONITORING_USE_GENEVA_CONFIG_SERVICE", "false"),
("ENABLE_MCS", "false")
])
ssl_cert_var_name, ssl_cert_var_value = get_ssl_cert_info('Enable')
default_configs[ssl_cert_var_name] = ssl_cert_var_value
_, _, _, az_environment, _ = me_handler.get_imds_values(is_lad=False)
if az_environment.lower() == me_handler.ArcACloudName:
_, mcs_endpoint = me_handler.get_arca_endpoints_from_himds()
default_configs["customRegionalEndpoint"] = mcs_endpoint
default_configs["customGlobalEndpoint"] = mcs_endpoint
default_configs["customResourceEndpoint"] = "https://monitoring.azs"
"""
Decide the mode and configuration. There are two supported configuration schema, mix-and-match between schemas is disallowed:
Legacy: allows one of [MCS, GCS single tenant, or GCS multi tenant ("Auto-Config")] modes
Next-Generation: allows MCS, GCS multi tenant, or both
"""
is_gcs_single_tenant = False
GcsEnabled, McsEnabled = get_control_plane_mode()
# Next-generation schema
if public_settings is not None and (public_settings.get(GenevaConfigKey) or public_settings.get(AzureMonitorConfigKey)):
geneva_configuration = public_settings.get(GenevaConfigKey)
azure_monitor_configuration = public_settings.get(AzureMonitorConfigKey)
# Check for mix-and match of next-generation and legacy schema content
if len(public_settings) > 1 and ((geneva_configuration and not azure_monitor_configuration) or (azure_monitor_configuration and not geneva_configuration)):
log_and_exit("Enable", MissingorInvalidParameterErrorCode, 'Mixing genevaConfiguration or azureMonitorConfiguration with other configuration schemas is not allowed')
if geneva_configuration and geneva_configuration.get("enable") == True:
hutil_log_info("Detected Geneva+ mode; azuremonitoragentmgr service will be started to handle Geneva tenants")
ensure["azuremonitoragentmgr"] = True
if azure_monitor_configuration and azure_monitor_configuration.get("enable") == True:
hutil_log_info("Detected Azure Monitor+ mode; azuremonitoragent service will be started to handle Azure Monitor tenant")
ensure["azuremonitoragent"] = True
azure_monitor_public_settings = azure_monitor_configuration.get("configuration")
azure_monitor_protected_settings = protected_settings.get(AzureMonitorConfigKey) if protected_settings is not None else None
handle_mcs_config(azure_monitor_public_settings, azure_monitor_protected_settings, default_configs)
# Legacy schema
elif public_settings is not None and public_settings.get("GCS_AUTO_CONFIG") == True:
hutil_log_info("Detected Auto-Config mode; azuremonitoragentmgr service will be started to handle Geneva tenants")
ensure["azuremonitoragentmgr"] = True
elif (protected_settings is None or len(protected_settings) == 0) or (public_settings is not None and "proxy" in public_settings and "mode" in public_settings.get("proxy") and public_settings.get("proxy").get("mode") == "application"):
hutil_log_info("Detected Azure Monitor mode; azuremonitoragent service will be started to handle Azure Monitor configuration")
ensure["azuremonitoragent"] = True
handle_mcs_config(public_settings, protected_settings, default_configs)
else:
hutil_log_info("Detected Geneva mode; azuremonitoragent service will be started to handle Geneva configuration")
ensure["azuremonitoragent"] = True
is_gcs_single_tenant = True
handle_gcs_config(public_settings, protected_settings, default_configs)
# generate local syslog configuration files as in auto config syslog is not driven from DCR
# Note that internally AMCS with geneva config path can be used in which case syslog should be handled same way as default 1P
# generate local syslog configuration files as in 1P syslog is not driven from DCR
if GcsEnabled:
generate_localsyslog_configs(uses_gcs=True, uses_mcs=McsEnabled)
config_file = "/etc/default/azuremonitoragent"
temp_config_file = "/etc/default/azuremonitoragent_temp"
try:
if os.path.isfile(config_file):
new_config = "\n".join(["export {0}={1}".format(key, value) for key, value in default_configs.items()]) + "\n"
with open(temp_config_file, "w") as f:
f.write(new_config)
if not os.path.isfile(temp_config_file):
log_and_exit("Enable", GenericErrorCode, "Error while updating environment variables in {0}".format(config_file))
os.remove(config_file)
os.rename(temp_config_file, config_file)
else:
log_and_exit("Enable", GenericErrorCode, "Could not find the file {0}".format(config_file))
except Exception as e:
log_and_exit("Enable", GenericErrorCode, "Failed to add environment variables to {0}: {1}".format(config_file, e))
if "ENABLE_MCS" in default_configs and default_configs["ENABLE_MCS"] == "true":
if platform.machine() != 'aarch64':
# enable processes for Custom Logs
ensure["azuremonitor-agentlauncher"] = True
ensure["azuremonitor-coreagent"] = True
# start the metrics and syslog watcher only in 3P mode
start_metrics_process()
start_syslogconfig_process()
elif ensure.get("azuremonitoragentmgr") or is_gcs_single_tenant:
# In GCS scenarios, ensure that AMACoreAgent is running
if platform.machine() != 'aarch64':
ensure["azuremonitor-coreagent"] = True
hutil_log_info('Handler initiating onboarding.')
if HUtilObject and HUtilObject.is_seq_smaller():
# Either upgrade has just happened (in which case we need to start), or enable was called with no change to extension config
hutil_log_info("Current sequence number, " + HUtilObject._context._seq_no + ", is not greater than the LKG sequence number. Starting service(s) only if it is not yet running.")
operations = ["start", "enable"]
else:
# Either this is a clean install (in which case restart is effectively start), or extension config has changed
hutil_log_info("Current sequence number, " + HUtilObject._context._seq_no + ", is greater than the LKG sequence number. Restarting service(s) to pick up the new config.")
operations = ["restart", "enable"]
output = ""
# Ensure non-required services are not running; do not block if this step fails
for service in [s for s in ensure.keys() if not ensure[s]]:
exit_code, disable_output = run_command_and_log(get_service_command(service, "stop", "disable"))
output += disable_output
for service in [s for s in ensure.keys() if ensure[s]]:
exit_code, enable_output = run_command_and_log(get_service_command(service, *operations))
output += enable_output
if exit_code != 0:
status_command = get_service_command(service, "status")
status_exit_code, status_output = run_command_and_log(status_command)
if status_exit_code != 0:
output += "Output of '{0}':\n{1}".format(status_command, status_output)
return exit_code, output
# check if .NET is installed to start Kql extension process
if platform.machine() != 'aarch64':
check_dotnet, dotnetcmd_output = run_command_and_log("dotnet --list-runtimes",log_cmd=False)
if check_dotnet != 0:
hutil_log_info(".NET 8.0 is not installed. Please install .NET 8.0 if you are using Kql transformation. See more here https://learn.microsoft.com/en-us/dotnet/core/install/linux")
#ensure kql extension service is not running. do not block if it fails
kql_exit_code, disable_output = run_command_and_log(get_service_command("azuremonitor-kqlextension", "stop", "disable"))
if kql_exit_code != 0:
status_command = get_service_command("azuremonitor-kqlextension", "status")
kql_exit_code, status_output = run_command_and_log(status_command)
hutil_log_info(status_output)
else:
if "8.0" in dotnetcmd_output:
hutil_log_info("Found .NET 8.0 installed")
if "ENABLE_MCS" in default_configs and default_configs["ENABLE_MCS"] == "true":
# start/enable kql extension only in 3P mode and non aarch64
kql_start_code, kql_output = run_command_and_log(get_service_command("azuremonitor-kqlextension", *operations))
output += kql_output # do not block if kql start fails
else:
hutil_log_info(".NET 8.0 is not installed. Please install .NET 8.0 if you are using Kql transformation. See more here https://learn.microsoft.com/en-us/dotnet/core/install/linux")
#ensure kql extension service is not running
kql_exit_code, disable_output = run_command_and_log(get_service_command("azuremonitor-kqlextension", "stop", "disable"))
if kql_exit_code != 0:
status_command = get_service_command("azuremonitor-kqlextension", "status")
kql_exit_code, status_output = run_command_and_log(status_command)
hutil_log_info(status_output)
# Service(s) were successfully configured and started; increment sequence number
HUtilObject.save_seq()
return exit_code, output
def handle_gcs_config(public_settings, protected_settings, default_configs):
"""
Populate the defaults for legacy-path GCS mode
"""
# look for LA protected settings
for var in list(protected_settings.keys()):
if "_key" in var or "_id" in var:
default_configs[var] = protected_settings.get(var)
# check if required GCS params are available
MONITORING_GCS_CERT_CERTFILE = None
if "certificate" in protected_settings:
MONITORING_GCS_CERT_CERTFILE = base64.standard_b64decode(protected_settings.get("certificate"))
if "certificatePath" in protected_settings:
try:
with open(protected_settings.get("certificatePath"), 'r') as f:
MONITORING_GCS_CERT_CERTFILE = f.read()
except Exception as ex:
log_and_exit('Enable', MissingorInvalidParameterErrorCode, 'Failed to read certificate {0}: {1}'.format(protected_settings.get("certificatePath"), ex))
MONITORING_GCS_CERT_KEYFILE = None
if "certificateKey" in protected_settings:
MONITORING_GCS_CERT_KEYFILE = base64.standard_b64decode(protected_settings.get("certificateKey"))
if "certificateKeyPath" in protected_settings:
try:
with open(protected_settings.get("certificateKeyPath"), 'r') as f:
MONITORING_GCS_CERT_KEYFILE = f.read()
except Exception as ex:
log_and_exit('Enable', MissingorInvalidParameterErrorCode, 'Failed to read certificate key {0}: {1}'.format(protected_settings.get("certificateKeyPath"), ex))
MONITORING_GCS_ENVIRONMENT = ""
if "monitoringGCSEnvironment" in protected_settings:
MONITORING_GCS_ENVIRONMENT = protected_settings.get("monitoringGCSEnvironment")
MONITORING_GCS_NAMESPACE = ""
if "namespace" in protected_settings:
MONITORING_GCS_NAMESPACE = protected_settings.get("namespace")
MONITORING_GCS_ACCOUNT = ""
if "monitoringGCSAccount" in protected_settings:
MONITORING_GCS_ACCOUNT = protected_settings.get("monitoringGCSAccount")
MONITORING_GCS_REGION = ""
if "monitoringGCSRegion" in protected_settings:
MONITORING_GCS_REGION = protected_settings.get("monitoringGCSRegion")
MONITORING_CONFIG_VERSION = ""
if "configVersion" in protected_settings:
MONITORING_CONFIG_VERSION = protected_settings.get("configVersion")
MONITORING_GCS_AUTH_ID_TYPE = ""
if "monitoringGCSAuthIdType" in protected_settings:
MONITORING_GCS_AUTH_ID_TYPE = protected_settings.get("monitoringGCSAuthIdType")
MONITORING_GCS_AUTH_ID = ""
if "monitoringGCSAuthId" in protected_settings:
MONITORING_GCS_AUTH_ID = protected_settings.get("monitoringGCSAuthId")
MONITORING_TENANT = ""
if "monitoringTenant" in protected_settings:
MONITORING_TENANT = protected_settings.get("monitoringTenant")
MONITORING_ROLE = ""
if "monitoringRole" in protected_settings:
MONITORING_ROLE = protected_settings.get("monitoringRole")
MONITORING_ROLE_INSTANCE = ""
if "monitoringRoleInstance" in protected_settings:
MONITORING_ROLE_INSTANCE = protected_settings.get("monitoringRoleInstance")
if ((MONITORING_GCS_CERT_CERTFILE is None or MONITORING_GCS_CERT_KEYFILE is None) and (MONITORING_GCS_AUTH_ID_TYPE == "")) or MONITORING_GCS_ENVIRONMENT == "" or MONITORING_GCS_NAMESPACE == "" or MONITORING_GCS_ACCOUNT == "" or MONITORING_GCS_REGION == "" or MONITORING_CONFIG_VERSION == "":
log_and_exit("Enable", MissingorInvalidParameterErrorCode, 'Not all required GCS parameters are provided')
else:
# set the values for GCS
default_configs["MONITORING_USE_GENEVA_CONFIG_SERVICE"] = "true"
default_configs["MONITORING_GCS_ENVIRONMENT"] = MONITORING_GCS_ENVIRONMENT
default_configs["MONITORING_GCS_NAMESPACE"] = MONITORING_GCS_NAMESPACE
default_configs["MONITORING_GCS_ACCOUNT"] = MONITORING_GCS_ACCOUNT
default_configs["MONITORING_GCS_REGION"] = MONITORING_GCS_REGION
default_configs["MONITORING_CONFIG_VERSION"] = MONITORING_CONFIG_VERSION
# write the certificate and key to disk
uid = pwd.getpwnam("syslog").pw_uid
gid = grp.getgrnam("syslog").gr_gid
if MONITORING_GCS_AUTH_ID_TYPE != "":
default_configs["MONITORING_GCS_AUTH_ID_TYPE"] = MONITORING_GCS_AUTH_ID_TYPE
if MONITORING_GCS_AUTH_ID != "":
default_configs["MONITORING_GCS_AUTH_ID"] = MONITORING_GCS_AUTH_ID
if MONITORING_GCS_CERT_CERTFILE is not None:
default_configs["MONITORING_GCS_CERT_CERTFILE"] = "/etc/opt/microsoft/azuremonitoragent/gcscert.pem"
with open("/etc/opt/microsoft/azuremonitoragent/gcscert.pem", "wb") as f:
f.write(MONITORING_GCS_CERT_CERTFILE)
os.chown("/etc/opt/microsoft/azuremonitoragent/gcscert.pem", uid, gid)
os.system('chmod {1} {0}'.format("/etc/opt/microsoft/azuremonitoragent/gcscert.pem", 400))
if MONITORING_GCS_CERT_KEYFILE is not None:
default_configs["MONITORING_GCS_CERT_KEYFILE"] = "/etc/opt/microsoft/azuremonitoragent/gcskey.pem"
with open("/etc/opt/microsoft/azuremonitoragent/gcskey.pem", "wb") as f:
f.write(MONITORING_GCS_CERT_KEYFILE)
os.chown("/etc/opt/microsoft/azuremonitoragent/gcskey.pem", uid, gid)
os.system('chmod {1} {0}'.format("/etc/opt/microsoft/azuremonitoragent/gcskey.pem", 400))
if MONITORING_TENANT != "":
default_configs["MONITORING_TENANT"] = MONITORING_TENANT
if MONITORING_ROLE != "":
default_configs["MONITORING_ROLE"] = MONITORING_ROLE
if MONITORING_TENANT != "":
default_configs["MONITORING_ROLE_INSTANCE"] = MONITORING_ROLE_INSTANCE
def handle_mcs_config(public_settings, protected_settings, default_configs):
"""
Populate the defaults for MCS mode
"""
default_configs["ENABLE_MCS"] = "true"
default_configs["PA_GIG_BRIDGE_MODE"] = "true"
# April 2022: PA_FLUENT_SOCKET_PORT setting is being deprecated in place of PA_DATA_PORT. Remove when AMA 1.17 and earlier no longer need servicing.
default_configs["PA_FLUENT_SOCKET_PORT"] = "13005"
# this port will be dynamic in future
default_configs["PA_DATA_PORT"] = "13005"
# fetch proxy settings
if public_settings is not None and "proxy" in public_settings and "mode" in public_settings.get("proxy") and public_settings.get("proxy").get("mode") == "application":
default_configs["MDSD_PROXY_MODE"] = "application"
if "address" in public_settings.get("proxy"):
default_configs["MDSD_PROXY_ADDRESS"] = public_settings.get("proxy").get("address")
else:
log_and_exit("Enable", MissingorInvalidParameterErrorCode, 'Parameter "address" is required in proxy public setting')
if "auth" in public_settings.get("proxy") and public_settings.get("proxy").get("auth") == True:
if protected_settings is not None and "proxy" in protected_settings and "username" in protected_settings.get("proxy") and "password" in protected_settings.get("proxy"):
default_configs["MDSD_PROXY_USERNAME"] = protected_settings.get("proxy").get("username")
default_configs["MDSD_PROXY_PASSWORD"] = protected_settings.get("proxy").get("password")
set_proxy(default_configs["MDSD_PROXY_ADDRESS"], default_configs["MDSD_PROXY_USERNAME"], default_configs["MDSD_PROXY_PASSWORD"])
else:
log_and_exit("Enable", MissingorInvalidParameterErrorCode, 'Parameter "username" and "password" not in proxy protected setting')
else:
set_proxy(default_configs["MDSD_PROXY_ADDRESS"], "", "")
# is this Arc? If so, check for proxy
if os.path.isfile(ArcSettingsFile):
f = open(ArcSettingsFile, "r")
data = f.read()
if (data != ''):
json_data = json.loads(data)
BypassProxy = False
if json_data is not None and "proxy.bypass" in json_data:
bypass = json_data["proxy.bypass"]
if bypass == "AMA":
BypassProxy = True
if not BypassProxy and json_data is not None and "proxy.url" in json_data:
url = json_data["proxy.url"]
# only non-authenticated proxy config is supported
if url != '':
default_configs["MDSD_PROXY_ADDRESS"] = url
set_proxy(default_configs["MDSD_PROXY_ADDRESS"], "", "")
# add managed identity settings if they were provided
identifier_name, identifier_value, error_msg = get_managed_identity()
if error_msg:
log_and_exit("Enable", MissingorInvalidParameterErrorCode, 'Failed to determine managed identity settings. {0}.'.format(error_msg))
if identifier_name and identifier_value:
default_configs["MANAGED_IDENTITY"] = "{0}#{1}".format(identifier_name, identifier_value)
def get_control_plane_mode():
"""
Identify which control plane is in use
"""
public_settings, protected_settings = get_settings()
GcsEnabled = False
McsEnabled = False
if public_settings is not None and (public_settings.get(GenevaConfigKey) or public_settings.get(AzureMonitorConfigKey)):
geneva_configuration = public_settings.get(GenevaConfigKey)
azure_monitor_configuration = public_settings.get(AzureMonitorConfigKey)
if geneva_configuration and geneva_configuration.get("enable") == True:
GcsEnabled = True
if azure_monitor_configuration and azure_monitor_configuration.get("enable") == True:
McsEnabled = True
# Legacy schema
elif public_settings is not None and public_settings.get("GCS_AUTO_CONFIG") == True:
GcsEnabled = True
elif (protected_settings is None or len(protected_settings) == 0) or (public_settings is not None and "proxy" in public_settings and "mode" in public_settings.get("proxy") and public_settings.get("proxy").get("mode") == "application"):
McsEnabled = True
else:
GcsEnabled = True
return GcsEnabled, McsEnabled
def disable():
"""
Disable Azure Monitor Linux Agent process on the VM.
Note: disable operation times out from WAAgent at 15 minutes
"""
#stop the metrics process
stop_metrics_process()
#stop syslog config watcher process
stop_syslogconfig_process()
# stop amacoreagent and agent launcher
hutil_log_info('Handler initiating Core Agent and agent launcher')
if is_systemd() and platform.machine() != 'aarch64':
exit_code, output = run_command_and_log('systemctl stop azuremonitor-coreagent && systemctl disable azuremonitor-coreagent')
exit_code, output = run_command_and_log('systemctl stop azuremonitor-agentlauncher && systemctl disable azuremonitor-agentlauncher')
# in case AL is not cleaning up properly
check_kill_process('/opt/microsoft/azuremonitoragent/bin/fluent-bit')
# Stop and disable systemd services so they are not started after system reboot.
for service in ["azuremonitoragent", "azuremonitoragentmgr"]:
exit_code, output = run_command_and_log(get_service_command(service, "stop", "disable"))
if exit_code != 0:
status_command = get_service_command(service, "status")
status_exit_code, status_output = run_command_and_log(status_command)
if status_exit_code != 0:
output += "Output of '{0}':\n{1}".format(status_command, status_output)
if platform.machine() != 'aarch64':
# stop kql extensionso that is not started after system reboot. Do not block if it fails.
kql_exit_code, disable_output = run_command_and_log(get_service_command("azuremonitor-kqlextension", "stop", "disable"))
if kql_exit_code != 0:
status_command = get_service_command("azuremonitor-kqlextension", "status")
kql_exit_code, kql_status_output = run_command_and_log(status_command)
hutil_log_info(kql_status_output)
return exit_code, output
def update():
"""
Update the current installation of AzureMonitorLinuxAgent
No logic to install the agent as agent -> install() will be called
with update because upgradeMode = "UpgradeWithInstall" set in HandlerManifest
"""
return 0, ""
def restart_launcher():
if platform.machine() == 'aarch64':
return
# start agent launcher
hutil_log_info('Handler initiating agent launcher')
if is_systemd():
exit_code, output = run_command_and_log('systemctl restart azuremonitor-agentlauncher && systemctl enable azuremonitor-agentlauncher')
def set_proxy(address, username, password):
"""
# Set proxy http_proxy env var in dependent services
"""
try:
http_proxy = address
address = address.replace("http://","")
if username:
http_proxy = "http://" + username + ":" + password + "@" + address
# Update Coreagent
run_command_and_log("mkdir -p /etc/systemd/system/azuremonitor-coreagent.service.d")
run_command_and_log("echo '[Service]' > /etc/systemd/system/azuremonitor-coreagent.service.d/proxy.conf")
run_command_and_log("echo 'Environment=\"http_proxy={0}\"' >> /etc/systemd/system/azuremonitor-coreagent.service.d/proxy.conf".format(http_proxy))
run_command_and_log("echo 'Environment=\"https_proxy={0}\"' >> /etc/systemd/system/azuremonitor-coreagent.service.d/proxy.conf".format(http_proxy))
os.system('chmod {1} {0}'.format("/etc/systemd/system/azuremonitor-coreagent.service.d/proxy.conf", 400))
# Update ME
run_command_and_log("mkdir -p /etc/systemd/system/metrics-extension.service.d")
run_command_and_log("echo '[Service]' > /etc/systemd/system/metrics-extension.service.d/proxy.conf")
run_command_and_log("echo 'Environment=\"http_proxy={0}\"' >> /etc/systemd/system/metrics-extension.service.d/proxy.conf".format(http_proxy))
run_command_and_log("echo 'Environment=\"https_proxy={0}\"' >> /etc/systemd/system/metrics-extension.service.d/proxy.conf".format(http_proxy))
os.system('chmod {1} {0}'.format("/etc/systemd/system/metrics-extension.service.d/proxy.conf", 400))
run_command_and_log("systemctl daemon-reload")
except:
log_and_exit("enable", MissingorInvalidParameterErrorCode, "Failed to update /etc/systemd/system/azuremonitor-coreagent.service.d and mkdir -p /etc/systemd/system/metrics-extension.service.d" )
def get_managed_identity():
"""
# Determine Managed Identity (MI) settings
# Nomenclature: Managed System Identity (MSI), System-Assigned Identity (SAI), User-Assigned Identity (UAI)
# Unspecified MI scenario: MSI returns SAI token if exists, otherwise returns UAI token if exactly one UAI exists, otherwise failure
# Specified MI scenario: MSI returns token for specified MI
# Returns identifier_name, identifier_value, and error message (if any)
"""
identifier_name = identifier_value = ""
public_settings, _ = get_settings()
if public_settings is not None and public_settings.get(AzureMonitorConfigKey):
azure_monitor_configuration = public_settings.get(AzureMonitorConfigKey)
if azure_monitor_configuration and azure_monitor_configuration.get("enable") == True:
public_settings = azure_monitor_configuration.get("configuration")
if public_settings is not None and "authentication" in public_settings and "managedIdentity" in public_settings.get("authentication"):
managedIdentity = public_settings.get("authentication").get("managedIdentity")
if "identifier-name" not in managedIdentity or "identifier-value" not in managedIdentity:
return identifier_name, identifier_value, 'Parameters "identifier-name" and "identifier-value" are both required in authentication.managedIdentity public setting'
identifier_name = managedIdentity.get("identifier-name")
identifier_value = managedIdentity.get("identifier-value")
if identifier_name not in ["client_id", "mi_res_id"]:
return identifier_name, identifier_value, 'Invalid identifier-name provided; must be "client_id" or "mi_res_id"'
if not identifier_value:
return identifier_name, identifier_value, 'Invalid identifier-value provided; cannot be empty'
if identifier_name in ["object_id", "client_id"]:
guid_re = re.compile(r'[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}')
if not guid_re.search(identifier_value):
return identifier_name, identifier_value, 'Invalid identifier-value provided for {0}; must be a GUID'.format(identifier_name)
return identifier_name, identifier_value, ""
def stop_metrics_process():
if telhandler.is_running(is_lad=False):
#Stop the telegraf and ME services
tel_out, tel_msg = telhandler.stop_telegraf_service(is_lad=False)
if tel_out:
hutil_log_info(tel_msg)
else:
hutil_log_error(tel_msg)
#Delete the telegraf and ME services
tel_rm_out, tel_rm_msg = telhandler.remove_telegraf_service(is_lad=False)
if tel_rm_out: