def _create_recurring_profile(autoscale_settings, profile_name, start, end, recurrence, capacity, copy_rules=None, timezone=None): from azure.mgmt.monitor.models import (AutoscaleProfile, Recurrence, RecurrentSchedule) from azure.cli.command_modules.monitor._autoscale_util import build_autoscale_profile, validate_autoscale_profile import dateutil from datetime import time import json def _build_recurrence(base, time): recurrence = Recurrence(frequency=base.frequency, schedule=RecurrentSchedule( time_zone=base.schedule.time_zone, days=base.schedule.days, hours=[time.hour], minutes=[time.minute])) return recurrence start_time = dateutil.parser.parse(start).time() if start else time( hour=0, minute=0) end_time = dateutil.parser.parse(end).time() if end else time(hour=23, minute=59) default_profile, autoscale_profile = build_autoscale_profile( autoscale_settings) validate_autoscale_profile(autoscale_profile, start_time, end_time, recurrence) start_profile = AutoscaleProfile(name=profile_name, capacity=capacity, rules=[], recurrence=_build_recurrence( recurrence, start_time)) _apply_copy_rules(autoscale_settings, start_profile, copy_rules) end_profile = AutoscaleProfile(name=json.dumps({ 'name': default_profile.name, 'for': profile_name }), capacity=default_profile.capacity, rules=default_profile.rules, recurrence=_build_recurrence( recurrence, end_time)) autoscale_settings.profiles.append(start_profile) autoscale_settings.profiles.append(end_profile)
def autoscale_create(client, resource, count, autoscale_name=None, resource_group_name=None, min_count=None, max_count=None, location=None, tags=None, disabled=None, actions=None, email_administrator=None, email_coadministrators=None): from azure.mgmt.monitor.models import (AutoscaleSettingResource, AutoscaleProfile, AutoscaleNotification, ScaleCapacity, EmailNotification, WebhookNotification) if not autoscale_name: from msrestazure.tools import parse_resource_id autoscale_name = parse_resource_id(resource)['name'] min_count = min_count or count max_count = max_count or count default_profile = AutoscaleProfile(name=DEFAULT_PROFILE_NAME, capacity=ScaleCapacity( default=count, minimum=min_count, maximum=max_count), rules=[]) notification = AutoscaleNotification(email=EmailNotification( custom_emails=[], send_to_subscription_administrator=email_administrator, send_to_subscription_co_administrators=email_coadministrators), webhooks=[]) for action in actions or []: if isinstance(action, EmailNotification): for email in action.custom_emails: notification.email.custom_emails.append(email) elif isinstance(action, WebhookNotification): notification.webhooks.append(action) autoscale = AutoscaleSettingResource( location=location, profiles=[default_profile], tags=tags, notifications=[notification], enabled=not disabled, autoscale_setting_resource_name=autoscale_name, target_resource_uri=resource) if not (min_count == count and max_count == count): logger.warning( 'Follow up with `az monitor autoscale rule create` to add scaling rules.' ) return client.create_or_update(resource_group_name, autoscale_name, autoscale)
def _create_fixed_profile(autoscale_settings, profile_name, start, end, capacity, copy_rules=None, timezone=None): from azure.mgmt.monitor.models import AutoscaleProfile, TimeWindow if not (start and end): from knack.util import CLIError raise CLIError('usage error: fixed schedule: --start DATETIME --end DATETIME') profile = AutoscaleProfile( name=profile_name, capacity=capacity, rules=[], fixed_date=TimeWindow(start=start, end=end, time_zone=timezone), ) _apply_copy_rules(autoscale_settings, profile, copy_rules) autoscale_settings.profiles.append(profile)
def create_autoscaling_settings(ExistingVmScaleSetName, VmScaleSetID, resourceGroupName): NewVmScaleSetName = VmScaleSetID.split('/')[-1] existing_asg = monitor_client.autoscale_settings.get( resource_group_name=resourceGroupName, autoscale_setting_name=ExistingVmScaleSetName) rules = [ ScaleRule( metric_trigger=MetricTrigger( metric_name=i.metric_trigger.metric_name, #metric_namespace=i.metric_trigger.additional_properties['metricNamespace'], metric_resource_uri=VmScaleSetID, time_grain=i.metric_trigger.time_grain, statistic=i.metric_trigger.statistic, time_window=i.metric_trigger.time_window, time_aggregation=i.metric_trigger.time_aggregation, operator=i.metric_trigger.operator, threshold=i.metric_trigger.threshold #dimensions = i.metric_trigger.additional_properties['dimensions'] ), scale_action=i.scale_action) for i in existing_asg.profiles[0].rules ] profile = AutoscaleProfile(name=existing_asg.profiles[0].name, capacity=existing_asg.profiles[0].capacity, rules=rules, fixed_date=None, recurrence=None) parameters = AutoscaleSettingResource( location=existing_asg.location, tags=existing_asg.tags, profiles=[profile], notifications=existing_asg.notifications, enabled=True, autoscale_setting_resource_name=NewVmScaleSetName, target_resource_uri=VmScaleSetID) new_asg = monitor_client.autoscale_settings.create_or_update( resource_group_name=resourceGroupName, autoscale_setting_name=NewVmScaleSetName, parameters=parameters)
def exec_module(self, **kwargs): for key in list(self.module_arg_spec.keys()) + ['tags']: setattr(self, key, kwargs[key]) results = None changed = False self.log('Fetching auto scale settings {0}'.format(self.name)) results = self.get_auto_scale() if results and self.state == 'absent': # delete changed = True if not self.check_mode: self.delete_auto_scale() elif self.state == 'present': if not self.location: # Set default location resource_group = self.get_resource_group(self.resource_group) self.location = resource_group.location resource_id = self.target if isinstance(self.target, dict): resource_id = format_resource_id( val=self.target['name'], subscription_id=self.target.get('subscription_id') or self.subscription_id, namespace=self.target['namespace'], types=self.target['types'], resource_group=self.target.get('resource_group') or self.resource_group) self.target = resource_id resource_name = self.name def create_rule_instance(params): rule = params.copy() rule['metric_resource_uri'] = rule.get('metric_resource_uri', self.target) rule['time_grain'] = timedelta( minutes=rule.get('time_grain', 0)) rule['time_window'] = timedelta( minutes=rule.get('time_window', 0)) rule['cooldown'] = timedelta(minutes=rule.get('cooldown', 0)) return ScaleRule(metric_trigger=MetricTrigger(**rule), scale_action=ScaleAction(**rule)) profiles = [ AutoscaleProfile( name=p.get('name'), capacity=ScaleCapacity(minimum=p.get('min_count'), maximum=p.get('max_count'), default=p.get('count')), rules=[ create_rule_instance(r) for r in p.get('rules') or [] ], fixed_date=TimeWindow( time_zone=p.get('fixed_date_timezone'), start=p.get('fixed_date_start'), end=p.get('fixed_date_end')) if p.get('fixed_date_timezone') else None, recurrence=Recurrence( frequency=p.get('recurrence_frequency'), schedule=(RecurrentSchedule( time_zone=p.get('recurrence_timezone'), days=p.get('recurrence_days'), hours=p.get('recurrence_hours'), minutes=p.get('recurrence_mins')))) if p.get('recurrence_frequency') and p['recurrence_frequency'] != 'None' else None) for p in self.profiles or [] ] notifications = [ AutoscaleNotification(email=EmailNotification(**n), webhooks=[ WebhookNotification(service_uri=w) for w in n.get('webhooks') or [] ]) for n in self.notifications or [] ] if not results: # create new changed = True else: # check changed resource_name = results.autoscale_setting_resource_name or self.name update_tags, tags = self.update_tags(results.tags) if update_tags: changed = True self.tags = tags if self.target != results.target_resource_uri: changed = True if self.enabled != results.enabled: changed = True profile_result_set = set( [str(profile_to_dict(p)) for p in results.profiles or []]) if profile_result_set != set( [str(profile_to_dict(p)) for p in profiles]): changed = True notification_result_set = set([ str(notification_to_dict(n)) for n in results.notifications or [] ]) if notification_result_set != set( [str(notification_to_dict(n)) for n in notifications]): changed = True if changed: # construct the instance will be send to create_or_update api results = AutoscaleSettingResource( location=self.location, tags=self.tags, profiles=profiles, notifications=notifications, enabled=self.enabled, autoscale_setting_resource_name=resource_name, target_resource_uri=self.target) if not self.check_mode: results = self.create_or_update_auto_scale(results) # results should be the dict of the instance self.results = auto_scale_to_dict(results) self.results['changed'] = changed return self.results
def create_auto_scale_profile( queue_uri: str, min: int, max: int, default: int, scale_out_amount: int, scale_out_cooldown: int, scale_in_amount: int, scale_in_cooldown: int, ) -> AutoscaleProfile: return AutoscaleProfile( name=str(uuid.uuid4()), capacity=ScaleCapacity(minimum=min, maximum=max, default=max), # Auto scale tuning guidance: # https://docs.microsoft.com/en-us/azure/architecture/best-practices/auto-scaling rules=[ ScaleRule( metric_trigger=MetricTrigger( metric_name="ApproximateMessageCount", metric_resource_uri=queue_uri, # Check every 15 minutes time_grain=timedelta(minutes=15), # The average amount of messages there are in the pool queue time_aggregation=TimeAggregationType.AVERAGE, statistic=MetricStatisticType.COUNT, # Over the past 15 minutes time_window=timedelta(minutes=15), # When there's more than 1 message in the pool queue operator=ComparisonOperationType.GREATER_THAN_OR_EQUAL, threshold=1, divide_per_instance=False, ), scale_action=ScaleAction( direction=ScaleDirection.INCREASE, type=ScaleType.CHANGE_COUNT, value=scale_out_amount, cooldown=timedelta(minutes=scale_out_cooldown), ), ), # Scale in ScaleRule( # Scale in if no work in the past 20 mins metric_trigger=MetricTrigger( metric_name="ApproximateMessageCount", metric_resource_uri=queue_uri, # Check every 10 minutes time_grain=timedelta(minutes=10), # The average amount of messages there are in the pool queue time_aggregation=TimeAggregationType.AVERAGE, statistic=MetricStatisticType.SUM, # Over the past 10 minutes time_window=timedelta(minutes=10), # When there's no messages in the pool queue operator=ComparisonOperationType.EQUALS, threshold=0, divide_per_instance=False, ), scale_action=ScaleAction( direction=ScaleDirection.DECREASE, type=ScaleType.CHANGE_COUNT, value=scale_in_amount, cooldown=timedelta(minutes=scale_in_cooldown), ), ), ], )
def build_autoscale_profile(autoscale_settings): """ Builds up a logical model of the autoscale weekly schedule. This then has to later be translated into objects that work with the Monitor autoscale API. """ from datetime import time import json from azure.mgmt.monitor.models import AutoscaleProfile def _validate_default_profile(default_profile, profile): if profile.capacity.default != default_profile.capacity.default or \ profile.capacity.minimum != default_profile.capacity.minimum or \ profile.capacity.maximum != default_profile.capacity.maximum: from knack.util import CLIError raise CLIError('unable to resolve default profile.') recurring_profiles = [x for x in autoscale_settings.profiles if x.recurrence] default_profiles = [x for x in autoscale_settings.profiles if not x.recurrence and not x.fixed_date] profile_schedule = { } # find the default profile and ensure that if there are multiple, they are consistent default_profile = default_profiles[0] if default_profiles else None for p in default_profiles: _validate_default_profile(default_profile, p) for profile in recurring_profiles: # portal creates extra default profiles with JSON names... # trying to stay compatible with that try: # portal-created "default" or end time json_name = json.loads(profile.name) sched_name = json_name['for'] end_time = time(hour=profile.recurrence.schedule.hours[0], minute=profile.recurrence.schedule.minutes[0]) if not default_profile: # choose this as default if it is the first default_profile = AutoscaleProfile( name='default', capacity=profile.capacity, rules=profile.rules ) else: # otherwise ensure it is consistent with the one chosen earlier _validate_default_profile(default_profile, profile) for day in profile.recurrence.schedule.days: if day not in profile_schedule: profile_schedule[day] = {} if sched_name in profile_schedule[day]: profile_schedule[day][sched_name]['end'] = end_time else: profile_schedule[day][sched_name] = {'end': end_time} except ValueError: # start time sched_name = profile.name start_time = time(hour=profile.recurrence.schedule.hours[0], minute=profile.recurrence.schedule.minutes[0]) for day in profile.recurrence.schedule.days: if day not in profile_schedule: profile_schedule[day] = {} if sched_name in profile_schedule[day]: profile_schedule[day][sched_name]['start'] = start_time profile_schedule[day][sched_name]['capacity'] = profile.capacity profile_schedule[day][sched_name]['rules'] = profile.rules else: profile_schedule[day][sched_name] = { 'start': start_time, 'capacity': profile.capacity, 'rules': profile.rules } return default_profile, profile_schedule