Skip to content

Commit a796918

Browse files
authored
Merge branch 'master' into versionchange_after_arc
2 parents 9d832f9 + fc8e28c commit a796918

15 files changed

+428
-109
lines changed

src/core/src/bootstrap/ConfigurationFactory.py

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
""" Configure factory. This module populates configuration based on package manager and environment, e.g. TEST/DEV/PROD"""
1818
from __future__ import print_function
1919
import os
20+
import time
2021
from core.src.bootstrap.Constants import Constants
2122
from core.src.bootstrap.EnvLayer import EnvLayer
2223

@@ -40,15 +41,25 @@
4041
from core.src.package_managers.ZypperPackageManager import ZypperPackageManager
4142

4243
from core.src.service_interfaces.LifecycleManager import LifecycleManager
44+
from core.src.service_interfaces.LifecycleManagerAzure import LifecycleManagerAzure
45+
from core.src.service_interfaces.LifecycleManagerArc import LifecycleManagerArc
4346
from core.src.service_interfaces.StatusHandler import StatusHandler
4447
from core.src.service_interfaces.TelemetryWriter import TelemetryWriter
4548

49+
try:
50+
import urllib2 as urlreq #Python 2.x
51+
except:
52+
import urllib.request as urlreq #Python 3.x
53+
4654

4755
class ConfigurationFactory(object):
4856
""" Class for generating module definitions. Configuration is list of key value pairs. Please DON'T change key name.
4957
DI container relies on the key name to find and resolve dependencies. If you do need change it, please make sure to
5058
update the key name in all places that reference it. """
5159
def __init__(self, log_file_path, real_record_path, recorder_enabled, emulator_enabled, events_folder):
60+
self.vm_cloud_type = self.get_vm_cloud_type()
61+
self.lifecycle_manager_component = self.get_lifecycle_manager_component(self.vm_cloud_type)
62+
5263
self.bootstrap_configurations = {
5364
'prod_config': self.new_bootstrap_configuration(Constants.PROD, log_file_path, real_record_path, recorder_enabled, emulator_enabled, events_folder),
5465
'dev_config': self.new_bootstrap_configuration(Constants.DEV, log_file_path, real_record_path, recorder_enabled, emulator_enabled, events_folder),
@@ -155,18 +166,21 @@ def new_bootstrap_configuration(config_env, log_file_path, real_record_path, rec
155166

156167
def new_prod_configuration(self, package_manager_name, package_manager_component):
157168
""" Base configuration for Prod V2. """
169+
158170
configuration = {
159171
'config_env': Constants.PROD,
160172
'package_manager_name': package_manager_name,
161173
'lifecycle_manager': {
162-
'component': LifecycleManager,
174+
'component': self.lifecycle_manager_component,
163175
'component_args': ['env_layer', 'execution_config', 'composite_logger', 'telemetry_writer'],
164176
'component_kwargs': {}
165177
},
166178
'status_handler': {
167179
'component': StatusHandler,
168180
'component_args': ['env_layer', 'execution_config', 'composite_logger', 'telemetry_writer'],
169-
'component_kwargs': {}
181+
'component_kwargs': {
182+
'vm_cloud_type': self.vm_cloud_type
183+
}
170184
},
171185
'package_manager': {
172186
'component': package_manager_component,
@@ -187,7 +201,7 @@ def new_prod_configuration(self, package_manager_name, package_manager_component
187201
},
188202
'patch_assessor': {
189203
'component': PatchAssessor,
190-
'component_args': ['env_layer', 'execution_config', 'composite_logger', 'telemetry_writer', 'status_handler', 'package_manager'],
204+
'component_args': ['env_layer', 'execution_config', 'composite_logger', 'telemetry_writer', 'status_handler', 'package_manager', 'lifecycle_manager'],
191205
'component_kwargs': {}
192206
},
193207
'patch_installer': {
@@ -216,7 +230,7 @@ def new_prod_configuration(self, package_manager_name, package_manager_component
216230
},
217231
'configure_patching_processor': {
218232
'component': ConfigurePatchingProcessor,
219-
'component_args': ['env_layer', 'execution_config', 'composite_logger', 'telemetry_writer', 'status_handler', 'package_manager', 'auto_assess_service_manager', 'auto_assess_timer_manager'],
233+
'component_args': ['env_layer', 'execution_config', 'composite_logger', 'telemetry_writer', 'status_handler', 'package_manager', 'auto_assess_service_manager', 'auto_assess_timer_manager', 'lifecycle_manager'],
220234
'component_kwargs': {}
221235
},
222236
'maintenance_window': {
@@ -240,4 +254,43 @@ def new_test_configuration(self, package_manager_name, package_manager_component
240254
configuration['config_env'] = Constants.TEST
241255
# perform desired modifications to configuration
242256
return configuration
257+
258+
def get_lifecycle_manager_component(self, vm_cloud_type):
259+
""" finding life cycle manager based on vm and returning component name added in the prod configuration """
260+
azure_lifecycle_manager_component = LifecycleManagerAzure
261+
arc_lifecycle_manager_component = LifecycleManagerArc
262+
if (vm_cloud_type == Constants.VMCloudType.AZURE):
263+
return azure_lifecycle_manager_component
264+
elif (vm_cloud_type == Constants.VMCloudType.ARC):
265+
return arc_lifecycle_manager_component
266+
267+
return azure_lifecycle_manager_component
268+
269+
def get_vm_cloud_type(self):
270+
""" detects vm type. logic taken from HCRP code: https://github.com/PowerShell/DesiredStateConfiguration/blob/dev/src/dsc/dsc_service/service_main.cpp#L115
271+
Todo: how to check this only when it is Auto Assessment operation??? """
272+
metadata_value = "True"
273+
user_agent_value = "ArcAgent"
274+
request = urlreq.Request(Constants.IMDS_END_POINT)
275+
request.add_header('Metadata', metadata_value)
276+
request.add_header('UserAgent', user_agent_value)
277+
print("\nTrying to connect IMDS end point. URL:{0}.".format(str(Constants.IMDS_END_POINT)))
278+
for i in range(0, Constants.MAX_IMDS_CONNECTION_RETRY_COUNT):
279+
try:
280+
res = urlreq.urlopen(request, timeout=2)
281+
print(res.get_code())
282+
if(res.getcode() == 200):
283+
print("Connection to IMDS end point successfully established. VMCloudType is Azure\n")
284+
return Constants.VMCloudType.AZURE
285+
else:
286+
raise
287+
except:
288+
""" Failed to connect to Azure IMDS endpoint. This is expected on Arc machine - but not expected on Azure machine."""
289+
if i < Constants.MAX_IMDS_CONNECTION_RETRY_COUNT - 1:
290+
print("Failed to connect to IMDS end point. [Retry Count={0}].".format(str(i)))
291+
time.sleep(i+1)
292+
else:
293+
print("Failed to connect IMDS end point. This is expected in ARC VM. VMCloudType is Arc\n")
294+
return Constants.VMCloudType.ARC
295+
243296
# endregion

src/core/src/bootstrap/Constants.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ class AutoAssessmentStates(EnumBackport):
155155
MAX_FILE_OPERATION_RETRY_COUNT = 5
156156
MAX_ASSESSMENT_RETRY_COUNT = 5
157157
MAX_INSTALLATION_RETRY_COUNT = 3
158+
MAX_IMDS_CONNECTION_RETRY_COUNT = 5
158159

159160
class PackageClassification(EnumBackport):
160161
UNCLASSIFIED = 'Unclassified'
@@ -185,6 +186,14 @@ class RebootStatus(EnumBackport):
185186
STARTED = "Started"
186187
COMPLETED = "Completed"
187188
FAILED = "Failed"
189+
190+
# Enum for VM Cloud Type
191+
class VMCloudType(EnumBackport):
192+
UNKNOWN = "Unknown"
193+
AZURE = "Azure"
194+
ARC = "Arc"
195+
196+
IMDS_END_POINT = "http://169.254.169.254/metadata/instance/compute?api-version=2019-06-01"
188197

189198
# StartedBy Patch Assessment Summary Status Values
190199
class PatchAssessmentSummaryStartedBy(EnumBackport):

src/core/src/core_logic/ConfigurePatchingProcessor.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020

2121
class ConfigurePatchingProcessor(object):
22-
def __init__(self, env_layer, execution_config, composite_logger, telemetry_writer, status_handler, package_manager, auto_assess_service_manager, auto_assess_timer_manager):
22+
def __init__(self, env_layer, execution_config, composite_logger, telemetry_writer, status_handler, package_manager, auto_assess_service_manager, auto_assess_timer_manager, lifecycle_manager):
2323
self.env_layer = env_layer
2424
self.execution_config = execution_config
2525

@@ -30,6 +30,7 @@ def __init__(self, env_layer, execution_config, composite_logger, telemetry_writ
3030
self.package_manager = package_manager
3131
self.auto_assess_service_manager = auto_assess_service_manager
3232
self.auto_assess_timer_manager = auto_assess_timer_manager
33+
self.lifecycle_manager = lifecycle_manager
3334

3435
self.current_auto_os_patch_state = Constants.AutomaticOsPatchStates.UNKNOWN
3536
self.current_auto_assessment_state = Constants.AutoAssessmentStates.UNKNOWN
@@ -128,6 +129,9 @@ def __report_consolidated_configure_patch_status(self, status=Constants.STATUS_T
128129
auto_assessment_state=self.current_auto_assessment_state)
129130

130131
def __raise_if_agent_incompatible(self):
132+
if self.lifecycle_manager.get_vm_cloud_type() == Constants.VMCloudType.ARC and self.execution_config.operation not in [Constants.ASSESSMENT, Constants.INSTALLATION]:
133+
self.composite_logger.log("Skipping agent compatibility check for Arc cloud type when operation is not manual")
134+
return
131135
if not self.telemetry_writer.is_agent_compatible():
132136
error_msg = Constants.TELEMETRY_AT_AGENT_NOT_COMPATIBLE_ERROR_MSG
133137
self.composite_logger.log_error(error_msg)

src/core/src/core_logic/PatchAssessor.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121

2222
class PatchAssessor(object):
2323
""" Wrapper class of a single patch assessment """
24-
def __init__(self, env_layer, execution_config, composite_logger, telemetry_writer, status_handler, package_manager):
24+
def __init__(self, env_layer, execution_config, composite_logger, telemetry_writer, status_handler, package_manager, lifecycle_manager):
2525
self.env_layer = env_layer
2626
self.execution_config = execution_config
2727

2828
self.composite_logger = composite_logger
2929
self.telemetry_writer = telemetry_writer
3030
self.status_handler = status_handler
31-
31+
self.lifecycle_manager = lifecycle_manager
3232
self.package_manager = package_manager
3333

3434
def start_assessment(self):
@@ -49,9 +49,13 @@ def start_assessment(self):
4949

5050
for i in range(0, Constants.MAX_ASSESSMENT_RETRY_COUNT):
5151
try:
52+
if self.lifecycle_manager is not None:
53+
self.lifecycle_manager.lifecycle_status_check() # may terminate the code abruptly, as designed
5254
packages, package_versions = self.package_manager.get_all_updates()
5355
self.telemetry_writer.write_event("Full assessment: " + str(packages), Constants.TelemetryEventLevel.Verbose)
5456
self.status_handler.set_package_assessment_status(packages, package_versions)
57+
if self.lifecycle_manager is not None:
58+
self.lifecycle_manager.lifecycle_status_check() # may terminate the code abruptly, as designed
5559
sec_packages, sec_package_versions = self.package_manager.get_security_updates()
5660
self.telemetry_writer.write_event("Security assessment: " + str(sec_packages), Constants.TelemetryEventLevel.Verbose)
5761
self.status_handler.set_package_assessment_status(sec_packages, sec_package_versions, "Security")
@@ -76,6 +80,9 @@ def start_assessment(self):
7680
return True
7781

7882
def raise_if_agent_incompatible(self):
83+
if self.lifecycle_manager.get_vm_cloud_type() == Constants.VMCloudType.ARC and self.execution_config.operation not in [Constants.ASSESSMENT, Constants.INSTALLATION]:
84+
self.composite_logger.log("Skipping agent compatibility check for Arc cloud type when operation is not manual")
85+
return
7986
if not self.telemetry_writer.is_agent_compatible():
8087
error_msg = Constants.TELEMETRY_AT_AGENT_NOT_COMPATIBLE_ERROR_MSG
8188
self.composite_logger.log_error(error_msg)

src/core/src/core_logic/PatchInstaller.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ def start_installation(self, simulate=False):
9999
return overall_patch_installation_successful
100100

101101
def raise_if_agent_incompatible(self):
102+
if self.lifecycle_manager.get_vm_cloud_type() == Constants.VMCloudType.ARC and self.execution_config.operation not in [Constants.ASSESSMENT, Constants.INSTALLATION]:
103+
self.composite_logger.log("Skipping agent compatibility check for Arc cloud type when operation is not manual")
104+
return
102105
if not self.telemetry_writer.is_agent_compatible():
103106
error_msg = Constants.TELEMETRY_AT_AGENT_NOT_COMPATIBLE_ERROR_MSG
104107
self.composite_logger.log_error(error_msg)

src/core/src/package_managers/PackageManager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ def get_package_size(self, output):
304304

305305
# region Package Manager Settings
306306
def get_package_manager_setting(self, setting_key, default_value='d5414abb-62f9-40e3-96e1-d579f85a79ba'):
307-
# type: (str, object) -> "" # type hinting to remove a warning
307+
# type: (str, object) -> "" # type hinting to remove a warning
308308
"""Gets any set package manager setting"""
309309
if setting_key in self.package_manager_settings:
310310
return self.package_manager_settings[setting_key]
@@ -316,7 +316,7 @@ def get_package_manager_setting(self, setting_key, default_value='d5414abb-62f9-
316316
raise Exception(error_msg, "[{0}]".format(Constants.ERROR_ADDED_TO_STATUS))
317317

318318
def set_package_manager_setting(self, setting_key, setting_value=""):
319-
# type: (str, object) -> "" # type hinting to remove a warning
319+
# type: (str, object) -> "" # type hinting to remove a warning
320320
"""Sets package manager setting"""
321321
self.package_manager_settings[setting_key] = setting_value
322322
# endregion

0 commit comments

Comments
 (0)