def __init__(self, infra_model_inst=None, provisioner=None, namespace_model_inst=None, config_model_inst=None, log_level=LOG_INFO, no_delay=False, num_threads=5, post_prov_pause=60): """ Create an instance of the orchestrator to operate on the supplied models/provisioner This method creates a new orchestrator, which is then ready to initiate the system described by the models provided to it. Some reasonableness checks are done on the arguments to ensure that there are no conflicting semantics being requested, otherwise the method simply returns an orchestrator ready to go. @keyword infra_model_instance: Optional; an instance of a subclass of L{actuator.infra.InfraModel}. If absent, all host information must be contained as values for host_refs in the NamespaceModel that resolve to either an IP address or a resolvable host name. @keyword provisioner: Optional; an instance of a subclass of L{actuator.provisioners.core.BaseProvisioner}, such as the Openstack provisioner. If absent and an infra model has been supplied, then all resources in the model must be StaticServers, or else the resource will not get provisioned. @keyword namespace_model_inst: Optional; an instance of a subclass of L{actuator.namespace.NamespaceModel}. If absent, then only infra model processing will be possible (if one was supplied). @keyword config_model_instance: Optional; an instance of a subclass of L{actuator.config.ConfigModel}. If absent, no configuration will be carried out, but the namespace can be interrogated after orchestration to determine values from any provisioned infra @keyword log_level: Optional; default is LOG_INFO. One of the symbolic log constants from the top level actuator package. These are LOG_CRIT, LOG_ERROR, LOG_WARN, LOG_INFO, and LOG_DEBUG. The default supplies progress on each provisioning and configuration task. @keyword no_delay: Optional; boolean, default is False. Flags if a short, random delay of up to 2.5 seconds should be inserted prior to performing a task. May be desirable in cases where many tasks may hit a single host at one time, which spreads out the load of establishing ssh connections, helping to avoid timeouts. @keyword num_threads: Optional; int, default is 5. Each task, whether resource provisioning or configuration, is carried out in a separate thread. The more parallel tasks your model has the higher this number can be to have a positive impact on the overall task completion rate. There is no value in making this larger than the largest number of tasks you may have running in parallel. @keyword post_prov_pause: Optional: int, default is 60. The number of seconds to pause after provision is done before starting on configuration. The reason this is useful is because virtual/cloud systems may complete provisioning, but they may not have all route information propagated for newly provisioned hosts/floating ips right away. This pause gives virtual/cloud systems a chance to stabilize before starting on configuration tasks. If no provisioning was done (a static infra model or simply no infra/provisioner), then the pause is skipped. @raise ExecutionException: In the following circumstances this method will raise actuator.ExecutionException: - The value supplied for infra_model_inst is not an instance of L{actuator.infra.InfraModel} - The value supplied for provisioner is not an instance of the L{actuator.provisioners.core.BaseProvisioner} base class - The value supplied for namespace_model_inst is not an instance of L{actuator.namespace.NamespaceModel} - The value supplied for config_model_inst is not an instance of L{actuator.config.ConfigModel} @return: initialized orchestrator instance """ if not (infra_model_inst is None or isinstance(infra_model_inst, InfraModel)): raise ExecutionException("infra_model_inst is no an instance of InfraModel") self.infra_model_inst = infra_model_inst if not (provisioner is None or isinstance(provisioner, BaseProvisioner)): raise ExecutionException("provisioner is not an instance of BaseProvisioner") self.provisioner = provisioner if not (namespace_model_inst is None or isinstance(namespace_model_inst, NamespaceModel)): raise ExecutionException("namespace_model_inst is not an instance of NamespaceModel") self.namespace_model_inst = namespace_model_inst if not (config_model_inst is None or isinstance(config_model_inst, ConfigModel)): raise ExecutionException("config_model_inst is not an instance of ConfigModel") self.config_model_inst = config_model_inst self.log_level = log_level root_logger.setLevel(log_level) self.logger = root_logger.getChild("orchestrator") self.post_prov_pause = post_prov_pause self.status = self.NOT_STARTED if self.config_model_inst is not None: self.config_ea = AnsibleExecutionAgent(config_model_instance=self.config_model_inst, namespace_model_instance=self.namespace_model_inst, num_threads=num_threads, no_delay=no_delay, log_level=log_level)
class ActuatorOrchestration(object): """ Processes Actuator models to stand up the system being model (initiate a system). This class provides the overall controls to process a set of Actuator models, along with a provisioner, to stand up an instance of they system they model. When the standup is being processed, log messages are sent to stdout using the standard Python logging module. You can change where messages are sent by installing a different handler on the root logger. See the logging section of the Python manual for more info. """ NOT_STARTED = 0 PERFORMING_PROVISION = 1 PERFORMING_CONFIG = 2 PERFORMING_EXEC = 3 COMPLETE = 4 ABORT_PROVISION = 5 ABORT_CONFIG = 6 ABORT_EXEC = 7 def __init__(self, infra_model_inst=None, provisioner=None, namespace_model_inst=None, config_model_inst=None, log_level=LOG_INFO, no_delay=False, num_threads=5, post_prov_pause=60): """ Create an instance of the orchestrator to operate on the supplied models/provisioner This method creates a new orchestrator, which is then ready to initiate the system described by the models provided to it. Some reasonableness checks are done on the arguments to ensure that there are no conflicting semantics being requested, otherwise the method simply returns an orchestrator ready to go. @keyword infra_model_instance: Optional; an instance of a subclass of L{actuator.infra.InfraModel}. If absent, all host information must be contained as values for host_refs in the NamespaceModel that resolve to either an IP address or a resolvable host name. @keyword provisioner: Optional; an instance of a subclass of L{actuator.provisioners.core.BaseProvisioner}, such as the Openstack provisioner. If absent and an infra model has been supplied, then all resources in the model must be StaticServers, or else the resource will not get provisioned. @keyword namespace_model_inst: Optional; an instance of a subclass of L{actuator.namespace.NamespaceModel}. If absent, then only infra model processing will be possible (if one was supplied). @keyword config_model_instance: Optional; an instance of a subclass of L{actuator.config.ConfigModel}. If absent, no configuration will be carried out, but the namespace can be interrogated after orchestration to determine values from any provisioned infra @keyword log_level: Optional; default is LOG_INFO. One of the symbolic log constants from the top level actuator package. These are LOG_CRIT, LOG_ERROR, LOG_WARN, LOG_INFO, and LOG_DEBUG. The default supplies progress on each provisioning and configuration task. @keyword no_delay: Optional; boolean, default is False. Flags if a short, random delay of up to 2.5 seconds should be inserted prior to performing a task. May be desirable in cases where many tasks may hit a single host at one time, which spreads out the load of establishing ssh connections, helping to avoid timeouts. @keyword num_threads: Optional; int, default is 5. Each task, whether resource provisioning or configuration, is carried out in a separate thread. The more parallel tasks your model has the higher this number can be to have a positive impact on the overall task completion rate. There is no value in making this larger than the largest number of tasks you may have running in parallel. @keyword post_prov_pause: Optional: int, default is 60. The number of seconds to pause after provision is done before starting on configuration. The reason this is useful is because virtual/cloud systems may complete provisioning, but they may not have all route information propagated for newly provisioned hosts/floating ips right away. This pause gives virtual/cloud systems a chance to stabilize before starting on configuration tasks. If no provisioning was done (a static infra model or simply no infra/provisioner), then the pause is skipped. @raise ExecutionException: In the following circumstances this method will raise actuator.ExecutionException: - The value supplied for infra_model_inst is not an instance of L{actuator.infra.InfraModel} - The value supplied for provisioner is not an instance of the L{actuator.provisioners.core.BaseProvisioner} base class - The value supplied for namespace_model_inst is not an instance of L{actuator.namespace.NamespaceModel} - The value supplied for config_model_inst is not an instance of L{actuator.config.ConfigModel} @return: initialized orchestrator instance """ if not (infra_model_inst is None or isinstance(infra_model_inst, InfraModel)): raise ExecutionException("infra_model_inst is no an instance of InfraModel") self.infra_model_inst = infra_model_inst if not (provisioner is None or isinstance(provisioner, BaseProvisioner)): raise ExecutionException("provisioner is not an instance of BaseProvisioner") self.provisioner = provisioner if not (namespace_model_inst is None or isinstance(namespace_model_inst, NamespaceModel)): raise ExecutionException("namespace_model_inst is not an instance of NamespaceModel") self.namespace_model_inst = namespace_model_inst if not (config_model_inst is None or isinstance(config_model_inst, ConfigModel)): raise ExecutionException("config_model_inst is not an instance of ConfigModel") self.config_model_inst = config_model_inst self.log_level = log_level root_logger.setLevel(log_level) self.logger = root_logger.getChild("orchestrator") self.post_prov_pause = post_prov_pause self.status = self.NOT_STARTED if self.config_model_inst is not None: self.config_ea = AnsibleExecutionAgent(config_model_instance=self.config_model_inst, namespace_model_instance=self.namespace_model_inst, num_threads=num_threads, no_delay=no_delay, log_level=log_level) def is_running(self): """ Predicate method to call to check if the initiation is still running. @return: True is the initiation is still running, False otherwise """ return self.status in [self.PERFORMING_CONFIG, self.PERFORMING_EXEC, self.PERFORMING_PROVISION] def is_complete(self): """ Predicate method to check if the run is complete with no error @return: True is the run completed successfully; False otherwise """ return self.status == self.COMPLETE def get_errors(self): """ Returns the error details of any failed tasks during initiation. @return: A list of 4-tuples: (t, et, ev, tb), where: t is the task that experienced the error et is the exception type ev is the exception value tb is the traceback from where the exception was raised """ errors = [] if self.status in [self.ABORT_CONFIG, self.ABORT_EXEC, self.ABORT_PROVISION]: if self.status == self.ABORT_PROVISION: errors = self.provisioner.agent.get_aborted_tasks() elif self.status == self.ABORT_CONFIG: errors = self.config_ea.get_aborted_tasks() return errors def initiate_system(self): """ Stand up (initiate) the system from the models Starts the process of system initiation. By default, logs progress messages to stdout. If errors are raised, then they will be logged with level CRITICAL. @return: True if initiation was successful, False otherwise. """ self.logger.info("Orchestration starting") did_provision = False if self.infra_model_inst is not None and self.provisioner is not None: try: self.status = self.PERFORMING_PROVISION self.logger.info("Starting provisioning phase") if self.namespace_model_inst: self.namespace_model_inst.set_infra_model(self.infra_model_inst) self.namespace_model_inst.compute_provisioning_for_environ(self.infra_model_inst) _ = self.infra_model_inst.refs_for_components() self.provisioner.provision_infra_model(self.infra_model_inst) self.logger.info("Provisioning phase complete") did_provision = True except ProvisionerException, e: self.status = self.ABORT_PROVISION self.logger.critical(">>> Provisioner failed " "with '%s'; failed resources shown below" % e.message) if self.provisioner.agent is not None: for t, et, ev, tb in self.provisioner.agent.get_aborted_tasks(): self.logger.critical("Task %s named %s id %s" % (t.__class__.__name__, t.name, str(t._id)), exc_info=(et, ev, tb)) else: self.logger.critical("No further information") self.logger.critical("Aborting orchestration") return False else: