<commit_before>
import uuid
import datetime

import paasmaker
from paasmaker.common.core import constants

from base import BaseHealthCheck, BaseHealthCheckTest

import colander

class AdjustInstancesHealthCheckParametersSchema(colander.MappingSchema):
	startup_wait = colander.SchemaNode(colander.Integer(),
		title="Startup Delay",
		description="The number of seconds after an application is RUNNING to start adjusting instances.",
		default=20,
		missing=20)

class AdjustInstancesHealthCheck(BaseHealthCheck):
	MODES = {
		paasmaker.util.plugin.MODE.HEALTH_CHECK: AdjustInstancesHealthCheckParametersSchema(),
	}
	API_VERSION = "0.9.0"

	def check(self, parent_job_id, callback, error_callback):
		self.callback = callback
		self.error_callback = error_callback
		self.parent_job_id = parent_job_id

		def got_session(session):
			self.session = session

			# Find applications in state RUNNING, and check their instances.
			# Only check applications that haven't been altered for
			# a short time; this stops race conditions around normal startup
			# conditions.
			older_than = datetime.datetime.utcnow() - datetime.timedelta(0, self.parameters['startup_wait'])

			self.running_versions = self.session.query(
				paasmaker.model.ApplicationVersion
			).filter(
				paasmaker.model.ApplicationVersion.state == constants.VERSION.RUNNING,
				paasmaker.model.ApplicationVersion.updated < older_than
			).all()

			self.active_nodes = self.session.query(
				paasmaker.model.Node.id
			).filter(
				paasmaker.model.Node.state == constants.NODE.ACTIVE
			)

			self.checked_version_count = len(self.running_versions)
			self.stop_actions = 0
			self.start_actions = 0
			self.stopped_instances = 0
			self.whole_start_version = None

			self.logger.info("Checking %d running versions.", self.checked_version_count)

			# Start processing them on the IO loop, so as to coordinate with
			# other things that the system is doing.
			self.configuration.io_loop.add_callback(self._get_next_version)

			# end of got_session()

		self.configuration.get_database_session(got_session, error_callback)

	def _get_next_version(self):
		if self.whole_start_version:
			# Need to kick off a start action for an entire
			# version. Do that now, and then move on.
			def submitted(job_id):
				self.whole_start_version = None
				self.start_actions += 1
				self._get_next_version()

			paasmaker.common.job.coordinate.startup.StartupRootJob.setup_version(
				self.configuration,
				self.whole_start_version,
				submitted,
				self.error_callback,
				parent=self.parent_job_id
			)
		else:
			try:
				version = self.running_versions.pop()
				self.whole_start_version = None

				self._check_version(version)

			except IndexError, ex:
				# No more to process.
				self._finish()

	def _check_version(self, version):
		self.instance_types = self.session.query(
			paasmaker.model.ApplicationInstanceType
		).filter(
			paasmaker.model.ApplicationInstanceType.application_version == version
		).all()
		self._get_next_instance_type()

	def _get_next_instance_type(self):
		try:
			instance_type = self.instance_types.pop()

			self._check_instance_type(instance_type)

		except IndexError, ex:
			# No more to process.
			self.configuration.io_loop.add_callback(self._get_next_version)

	def _check_instance_type(self, instance_type):
		# Find how many are running only. Because this is obviously our
		# target state.
		adjustment_quantity = instance_type.adjustment_instances(
			self.session,
			states=[constants.INSTANCE.RUNNING]
		)

		self.logger.info(
			"%s, version %d, type %s, adjustment required: %d.",
			instance_type.application_version.application.name,
			instance_type.application_version.version,
			instance_type.name,
			adjustment_quantity
		)

		if adjustment_quantity == 0:
			# No action required.
			self.configuration.io_loop.add_callback(self._get_next_instance_type)

		elif adjustment_quantity > 0:
			# We need more instances.
			# If we perform the start job, it will assign and allocate more instances
			# as needed. But the start job only works against an entire version...
			# Mark it as needing to start. Another part will pick it up.
			self.whole_start_version = instance_type.application_version
			self.configuration.io_loop.add_callback(self._get_next_instance_type)

		elif adjustment_quantity < 0:
			# We need less instances.
			# Don't deregister them at this stage, just stop them.
			# TODO: Probably should de-register the instances. Or another
			# health check comes along to do that for us?

			# Fetch all the instances, and then rank them.
			instances = self.session.query(
				paasmaker.model.ApplicationInstance
			).filter(
				paasmaker.model.ApplicationInstance.application_instance_type_id == instance_type.id,
				paasmaker.model.ApplicationInstance.node_id.in_(self.active_nodes)
			).all()

			def rank_by_node(a, b):
				return int((a.node.score * 100) - (b.node.score * 100))

			instances = sorted(instances, cmp=rank_by_node)

			# Now extract the worst instances to stop.
			instances = instances[adjustment_quantity:]

			self.stop_actions += 1
			self.stopped_instances += len(instances)

			paasmaker.common.job.coordinate.shutdown.ShutdownRootJob.setup_instances(
				self.configuration,
				instances,
				self._instance_type_job_submitted,
				self.error_callback,
				parent=self.parent_job_id
			)

	def _instance_type_job_submitted(self, job_id):
		self.logger.info("Added corrective job, root %s", job_id)
		self.configuration.io_loop.add_callback(self._get_next_instance_type)

	def _finish(self):
		# Make this job tree executable.
		def done_executable():
			self.session.close()
			self.callback(
				{
					'adjusted_checked_versions': self.checked_version_count,
					'adjusted_stop_actions': self.stop_actions,
					'adjusted_start_actions': self.start_actions,
					'adjusted_stopped_instances': self.stopped_instances
				},
				"Completed. %d versions checked, %d stop requests (%d instances), %d start requests." % (self.checked_version_count, self.stop_actions, self.stopped_instances, self.start_actions)
			)

		self.configuration.job_manager.allow_execution(self.parent_job_id, done_executable)

class AdjustInstancesHealthCheckTest(BaseHealthCheckTest):
<commit_msg>Added an additional information message to the adjust instances plugin.<commit_after>
import uuid
import datetime

import paasmaker
from paasmaker.common.core import constants

from base import BaseHealthCheck, BaseHealthCheckTest

import colander

class AdjustInstancesHealthCheckParametersSchema(colander.MappingSchema):
	startup_wait = colander.SchemaNode(colander.Integer(),
		title="Startup Delay",
		description="The number of seconds after an application is RUNNING to start adjusting instances.",
		default=20,
		missing=20)

class AdjustInstancesHealthCheck(BaseHealthCheck):
	MODES = {
		paasmaker.util.plugin.MODE.HEALTH_CHECK: AdjustInstancesHealthCheckParametersSchema(),
	}
	API_VERSION = "0.9.0"

	def check(self, parent_job_id, callback, error_callback):
		self.callback = callback
		self.error_callback = error_callback
		self.parent_job_id = parent_job_id

		def got_session(session):
			self.session = session

			# Find applications in state RUNNING, and check their instances.
			# Only check applications that haven't been altered for
			# a short time; this stops race conditions around normal startup
			# conditions.
			older_than = datetime.datetime.utcnow() - datetime.timedelta(0, self.parameters['startup_wait'])

			self.running_versions = self.session.query(
				paasmaker.model.ApplicationVersion
			).filter(
				paasmaker.model.ApplicationVersion.state == constants.VERSION.RUNNING,
				paasmaker.model.ApplicationVersion.updated < older_than
			).all()

			self.active_nodes = self.session.query(
				paasmaker.model.Node.id
			).filter(
				paasmaker.model.Node.state == constants.NODE.ACTIVE
			)

			self.checked_version_count = len(self.running_versions)
			self.stop_actions = 0
			self.start_actions = 0
			self.stopped_instances = 0
			self.whole_start_version = None

			self.logger.info("Checking %d running versions.", self.checked_version_count)

			# Start processing them on the IO loop, so as to coordinate with
			# other things that the system is doing.
			self.configuration.io_loop.add_callback(self._get_next_version)

			# end of got_session()

		self.configuration.get_database_session(got_session, error_callback)

	def _get_next_version(self):
		if self.whole_start_version:
			# Need to kick off a start action for an entire
			# version. Do that now, and then move on.
			def submitted(job_id):
				self.whole_start_version = None
				self.start_actions += 1
				self._get_next_version()

			paasmaker.common.job.coordinate.startup.StartupRootJob.setup_version(
				self.configuration,
				self.whole_start_version,
				submitted,
				self.error_callback,
				parent=self.parent_job_id
			)
		else:
			try:
				version = self.running_versions.pop()
				self.whole_start_version = None

				self._check_version(version)

			except IndexError, ex:
				# No more to process.
				self._finish()

	def _check_version(self, version):
		self.instance_types = self.session.query(
			paasmaker.model.ApplicationInstanceType
		).filter(
			paasmaker.model.ApplicationInstanceType.application_version == version
		).all()
		self._get_next_instance_type()

	def _get_next_instance_type(self):
		try:
			instance_type = self.instance_types.pop()

			self._check_instance_type(instance_type)

		except IndexError, ex:
			# No more to process.
			self.configuration.io_loop.add_callback(self._get_next_version)

	def _check_instance_type(self, instance_type):
		# Find how many are running only. Because this is obviously our
		# target state.
		adjustment_quantity = instance_type.adjustment_instances(
			self.session,
			states=[constants.INSTANCE.RUNNING]
		)

		self.logger.info(
			"%s, version %d, type %s, adjustment required: %d.",
			instance_type.application_version.application.name,
			instance_type.application_version.version,
			instance_type.name,
			adjustment_quantity
		)

		if adjustment_quantity == 0:
			# No action required.
			self.configuration.io_loop.add_callback(self._get_next_instance_type)

		elif adjustment_quantity > 0:
			# We need more instances.
			# If we perform the start job, it will assign and allocate more instances
			# as needed. But the start job only works against an entire version...
			# Mark it as needing to start. Another part will pick it up.
			self.whole_start_version = instance_type.application_version
			self.configuration.io_loop.add_callback(self._get_next_instance_type)

		elif adjustment_quantity < 0:
			# We need less instances.
			# Don't deregister them at this stage, just stop them.
			# TODO: Probably should de-register the instances. Or another
			# health check comes along to do that for us?

			# Fetch all the instances, and then rank them.
			instances = self.session.query(
				paasmaker.model.ApplicationInstance
			).filter(
				paasmaker.model.ApplicationInstance.application_instance_type_id == instance_type.id,
				paasmaker.model.ApplicationInstance.node_id.in_(self.active_nodes)
			).all()

			def rank_by_node(a, b):
				return int((a.node.score * 100) - (b.node.score * 100))

			instances = sorted(instances, cmp=rank_by_node)

			# Now extract the worst instances to stop.
			instances = instances[adjustment_quantity:]

			self.stop_actions += 1
			self.stopped_instances += len(instances)

			paasmaker.common.job.coordinate.shutdown.ShutdownRootJob.setup_instances(
				self.configuration,
				instances,
				self._instance_type_job_submitted,
				self.error_callback,
				parent=self.parent_job_id
			)

	def _instance_type_job_submitted(self, job_id):
		self.logger.info("Added corrective job, root %s", job_id)
		self.configuration.io_loop.add_callback(self._get_next_instance_type)

	def _finish(self):
		# Make this job tree executable.
		def done_executable():
			self.logger.info("Completed checking instances. All adjustment jobs scheduled.")
			self.session.close()
			self.callback(
				{
					'adjusted_checked_versions': self.checked_version_count,
					'adjusted_stop_actions': self.stop_actions,
					'adjusted_start_actions': self.start_actions,
					'adjusted_stopped_instances': self.stopped_instances
				},
				"Completed. %d versions checked, %d stop requests (%d instances), %d start requests." % (self.checked_version_count, self.stop_actions, self.stopped_instances, self.start_actions)
			)

		self.configuration.job_manager.allow_execution(self.parent_job_id, done_executable)

class AdjustInstancesHealthCheckTest(BaseHealthCheckTest):

{'fraction_non_alphanumeric': 0.08659614777391854, 'fraction_numerical': 0.0034733185980423114, 'mean_word_length': 4.325346784363178, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3128945', 'n_tokens_mistral': 4512, 'n_tokens_neox': 3806, 'n_words': 892}