1# -------------------------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for license information. 4# -------------------------------------------------------------------------------------------- 5 6# pylint: disable=line-too-long,too-many-lines 7 8import os 9import time 10 11from azure.cli.core.util import get_file_json 12from azure.core.exceptions import HttpResponseError 13from azure.mgmt.servicefabric.models import (ApplicationTypeResource, 14 ApplicationTypeVersionResource, 15 ApplicationResource, 16 ApplicationUpgradePolicy, 17 ArmRollingUpgradeMonitoringPolicy, 18 ArmApplicationHealthPolicy, 19 ArmServiceTypeHealthPolicy) 20from azure.cli.command_modules.servicefabric._arm_deployment_utils import validate_and_deploy_arm_template 21 22from knack.log import get_logger 23 24logger = get_logger(__name__) 25 26 27def create_app(client, 28 resource_group_name, 29 cluster_name, 30 application_type_name, 31 application_type_version, 32 application_name, 33 package_url=None, 34 application_parameters=None, 35 minimum_nodes=None, 36 maximum_nodes=None): 37 if package_url is not None: 38 create_app_type_version(client, resource_group_name, cluster_name, application_type_name, application_type_version, package_url) 39 40 try: 41 apps = client.applications.list(resource_group_name, cluster_name) 42 for app in apps.value: 43 if app.name.lower() == application_name.lower(): 44 logger.info("Application '%s' already exists", application_name) 45 return app 46 47 appResource = ApplicationResource(type_name=application_type_name, 48 type_version=application_type_version, 49 minimum_nodes=minimum_nodes, 50 maximum_nodes=maximum_nodes, 51 parameters=application_parameters) 52 appResource.name = application_name 53 app = client.applications.begin_create_or_update(resource_group_name, cluster_name, application_name, appResource).result() 54 return app 55 except HttpResponseError as ex: 56 logger.error("HttpResponseError: %s", ex) 57 raise 58 59 60def update_app(client, 61 resource_group_name, 62 cluster_name, 63 application_name, 64 application_type_version=None, 65 application_parameters=None, 66 minimum_nodes=None, 67 maximum_nodes=None, 68 force_restart=False, 69 upgrade_replica_set_check_timeout=None, 70 failure_action=None, 71 health_check_retry_timeout=None, 72 health_check_wait_duration=None, 73 health_check_stable_duration=None, 74 upgrade_domain_timeout=None, 75 upgrade_timeout=None, 76 consider_warning_as_error=False, 77 default_service_type_max_percent_unhealthy_partitions_per_service=None, 78 default_service_type_max_percent_unhealthy_replicas_per_partition=None, 79 default_service_type_max_percent_unhealthy_services=None, 80 max_percent_unhealthy_deployed_applications=None, 81 service_type_health_policy_map=None): 82 try: 83 currentApp = client.applications.get(resource_group_name, cluster_name, application_name) 84 appResource = currentApp 85 # TODO: change to patch once the fix is deployed in the rp 86 # appResourceUpdate: ApplicationResourceUpdate = ApplicationResourceUpdate() 87 88 if application_type_version: 89 appResource.type_version = application_type_version 90 if application_parameters: 91 appResource.parameters.update(application_parameters) 92 if minimum_nodes is not None: 93 appResource.minimum_nodes = minimum_nodes 94 if maximum_nodes is not None: 95 appResource.maximum_nodes = maximum_nodes 96 97 appResource.upgrade_policy = _set_uprade_policy(currentApp.upgrade_policy, 98 force_restart, 99 upgrade_replica_set_check_timeout, 100 failure_action, 101 health_check_retry_timeout, 102 health_check_wait_duration, 103 health_check_stable_duration, 104 upgrade_domain_timeout, 105 upgrade_timeout, 106 consider_warning_as_error, 107 default_service_type_max_percent_unhealthy_partitions_per_service, 108 default_service_type_max_percent_unhealthy_replicas_per_partition, 109 default_service_type_max_percent_unhealthy_services, 110 max_percent_unhealthy_deployed_applications, 111 service_type_health_policy_map) 112 113 # TODO: change to patch once the fix is deployed in the rp 114 # client.applications.update(resource_group_name, cluster_name, application_name, appResourceUpdate) 115 return client.applications.begin_create_or_update(resource_group_name, cluster_name, application_name, appResource).result() 116 except HttpResponseError as ex: 117 logger.error("HttpResponseError: %s", ex) 118 raise 119 120 121def create_app_type(client, resource_group_name, cluster_name, application_type_name): 122 try: 123 appTypes = client.application_types.list(resource_group_name, cluster_name) 124 for appType in appTypes.value: 125 if appType.name.lower() == application_type_name.lower(): 126 logger.info("Application type '%s' already exists", application_type_name) 127 return appType 128 129 appTypeResource = ApplicationTypeResource() 130 logger.info("Creating application type '%s'", application_type_name) 131 return client.application_types.create_or_update(resource_group_name, cluster_name, application_type_name, appTypeResource) 132 except HttpResponseError as ex: 133 logger.error("HttpResponseError: %s", ex) 134 raise 135 136 137def create_app_type_version(client, 138 resource_group_name, 139 cluster_name, 140 application_type_name, 141 version, 142 package_url): 143 create_app_type(client, resource_group_name, cluster_name, application_type_name) 144 try: 145 appTypeVerions = client.application_type_versions.list(resource_group_name, cluster_name, application_type_name) 146 for appTypeVerion in appTypeVerions.value: 147 if appTypeVerion.name.lower() == version.lower(): 148 logger.info("Application type version '%s' already exists", version) 149 return appTypeVerion 150 151 appTypeVersionResource = ApplicationTypeVersionResource(app_package_url=package_url) 152 logger.info("Creating application type version %s:%s", application_type_name, version) 153 return client.application_type_versions.begin_create_or_update(resource_group_name, 154 cluster_name, 155 application_type_name, 156 version, 157 appTypeVersionResource).result() 158 except HttpResponseError as ex: 159 logger.error("HttpResponseError: %s", ex) 160 raise 161 162 163def create_service(cmd, 164 client, 165 resource_group_name, 166 cluster_name, 167 application_name, 168 service_name, 169 service_type, 170 state, 171 instance_count=None, 172 target_replica_set_size=None, 173 min_replica_set_size=None, 174 default_move_cost=None, 175 partition_scheme='singleton'): 176 parameter_file, template_file = _get_template_file_and_parameters_file() 177 template = get_file_json(template_file) 178 parameters = get_file_json(parameter_file)['parameters'] 179 180 # set params 181 _set_parameters(parameters, "clusterName", cluster_name) 182 _set_parameters(parameters, "applicationName", application_name) 183 _set_parameters(parameters, "serviceName", service_name) 184 185 _set_service_parameters(template, parameters, "serviceTypeName", service_type, "string") 186 187 if partition_scheme == 'singleton': 188 _set_service_parameters(template, parameters, "partitionDescription", {"partitionScheme": "Singleton"}, "object") 189 elif partition_scheme == 'uniformInt64': 190 _set_service_parameters(template, parameters, "partitionDescription", {"partitionScheme": "UniformInt64Range"}, "object") 191 elif partition_scheme == 'named': 192 _set_service_parameters(template, parameters, "partitionDescription", {"partitionScheme": "Named"}, "object") 193 194 if state == 'stateless': 195 _set_service_parameters(template, parameters, "instanceCount", int(instance_count), "int") 196 else: 197 _set_service_parameters(template, parameters, "targetReplicaSetSize", int(target_replica_set_size), "int") 198 _set_service_parameters(template, parameters, "minReplicaSetSize", int(min_replica_set_size), "int") 199 200 if default_move_cost: 201 _set_service_parameters(template, parameters, "defaultMoveCost", default_move_cost, "string") 202 203 validate_and_deploy_arm_template(cmd, resource_group_name, template, parameters) 204 205 return client.services.get(resource_group_name, cluster_name, application_name, service_name) 206 207 208def _get_template_file_and_parameters_file(): 209 script_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 210 template_parameter_folder = os.path.join('template', 'service') 211 parameter_file = os.path.join( 212 script_dir, template_parameter_folder, 'parameter.json') 213 template_file = os.path.join( 214 script_dir, template_parameter_folder, 'template.json') 215 return parameter_file, template_file 216 217 218def _set_service_parameters(template, parameters, name, value, param_type): 219 tempalte_parameters = template['parameters'] 220 if name not in tempalte_parameters: 221 tempalte_parameters[name] = {} 222 tempalte_parameters[name]["type"] = param_type 223 tempalte_resources_properties = template['resources'][0]['properties'] 224 tempalte_resources_properties[name] = "[parameters('{}')]".format(name) 225 _set_parameters(parameters, name, value) 226 227 228def _set_parameters(parameters, name, value): 229 if name not in parameters: 230 parameters[name] = {} 231 parameters[name]["value"] = value 232 233 234def _set_uprade_policy(current_upgrade_policy, 235 force_restart, 236 upgrade_replica_set_check_timeout, 237 failure_action, 238 health_check_retry_timeout, 239 health_check_wait_duration, 240 health_check_stable_duration, 241 upgrade_domain_timeout, 242 upgrade_timeout, 243 consider_warning_as_error, 244 default_service_type_max_percent_unhealthy_partitions_per_service, 245 default_service_type_max_percent_unhealthy_replicas_per_partition, 246 default_max_percent_service_type_unhealthy_services, 247 max_percent_unhealthy_deployed_applications, 248 service_type_health_policy_map): 249 if current_upgrade_policy is None: 250 current_upgrade_policy = ApplicationUpgradePolicy() 251 252 if force_restart: 253 current_upgrade_policy.force_restart = force_restart 254 if upgrade_replica_set_check_timeout is not None: 255 current_upgrade_policy.upgrade_replica_set_check_timeout = time.strftime('%H:%M:%S', time.gmtime(upgrade_replica_set_check_timeout)) 256 257 # RollingUpgradeMonitoringPolicy 258 if current_upgrade_policy.rolling_upgrade_monitoring_policy is None: 259 # initialize with defaults 260 current_upgrade_policy.rolling_upgrade_monitoring_policy \ 261 = ArmRollingUpgradeMonitoringPolicy(failure_action='Manual', 262 health_check_stable_duration=time.strftime('%H:%M:%S', time.gmtime(120)), 263 health_check_retry_timeout=time.strftime('%H:%M:%S', time.gmtime(600)), 264 health_check_wait_duration=time.strftime('%H:%M:%S', time.gmtime(0)), 265 upgrade_timeout=time.strftime('%H:%M:%S', time.gmtime(86399)), 266 upgrade_domain_timeout=time.strftime('%H:%M:%S', time.gmtime(86399))) 267 268 if failure_action: 269 current_upgrade_policy.rolling_upgrade_monitoring_policy.failure_action = failure_action 270 if health_check_stable_duration is not None: 271 current_upgrade_policy.rolling_upgrade_monitoring_policy.health_check_stable_duration = time.strftime('%H:%M:%S', time.gmtime(health_check_stable_duration)) 272 if health_check_retry_timeout is not None: 273 current_upgrade_policy.rolling_upgrade_monitoring_policy.health_check_retry_timeout = time.strftime('%H:%M:%S', time.gmtime(health_check_retry_timeout)) 274 if health_check_wait_duration is not None: 275 current_upgrade_policy.rolling_upgrade_monitoring_policy.health_check_wait_duration = time.strftime('%H:%M:%S', time.gmtime(health_check_wait_duration)) 276 if upgrade_timeout is not None: 277 current_upgrade_policy.rolling_upgrade_monitoring_policy.upgrade_timeout = time.strftime('%H:%M:%S', time.gmtime(upgrade_timeout)) 278 if upgrade_domain_timeout is not None: 279 current_upgrade_policy.rolling_upgrade_monitoring_policy.upgrade_domain_timeout = time.strftime('%H:%M:%S', time.gmtime(upgrade_domain_timeout)) 280 281 # ApplicationHealthPolicy 282 if current_upgrade_policy.application_health_policy is None: 283 current_upgrade_policy.application_health_policy = ArmApplicationHealthPolicy() 284 285 if consider_warning_as_error: 286 current_upgrade_policy.application_health_policy.consider_warning_as_error = True 287 288 if current_upgrade_policy.application_health_policy.default_service_type_health_policy is None: 289 current_upgrade_policy.application_health_policy.default_service_type_health_policy = ArmServiceTypeHealthPolicy( 290 max_percent_unhealthy_partitions_per_service=default_service_type_max_percent_unhealthy_partitions_per_service, 291 max_percent_unhealthy_replicas_per_partition=default_service_type_max_percent_unhealthy_replicas_per_partition, 292 max_percent_unhealthy_services=default_max_percent_service_type_unhealthy_services) 293 else: 294 if default_service_type_max_percent_unhealthy_partitions_per_service: 295 current_upgrade_policy.application_health_policy.default_service_type_health_policy .max_percent_unhealthy_partitions_per_service \ 296 = default_service_type_max_percent_unhealthy_partitions_per_service 297 if default_service_type_max_percent_unhealthy_replicas_per_partition: 298 current_upgrade_policy.application_health_policy.default_service_type_health_policy.max_percent_unhealthy_replicas_per_partition \ 299 = default_service_type_max_percent_unhealthy_replicas_per_partition 300 if default_max_percent_service_type_unhealthy_services: 301 current_upgrade_policy.application_health_policy.default_service_type_health_policy.max_percent_unhealthy_partitions_per_service \ 302 = default_max_percent_service_type_unhealthy_services 303 304 if max_percent_unhealthy_deployed_applications: 305 current_upgrade_policy.ApplicationHealthPolicy.max_percent_unhealthy_deployed_applications \ 306 = max_percent_unhealthy_deployed_applications 307 308 if service_type_health_policy_map: 309 current_upgrade_policy.application_health_policy.service_type_health_policy_map = service_type_health_policy_map 310 return current_upgrade_policy 311