<commit_before>#
# Copyright (c) 2009 RightScale Inc
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

class InstanceSetup

  include Nanite::Actor

  expose :report_state

  # Number of seconds to wait before switching to offline mode
  RECONNECT_GRACE_PERIOD = 30

  # Boot if and only if instance state is 'booting'
  #
  # === Parameters
  # agent_identity<String>:: Serialized agent identity for current agent
  def initialize(agent_identity)
    @boot_retries = 0
    @agent_identity = agent_identity
    RightScale::InstanceState.init(agent_identity)
    RightScale::RightLinkLog.force_debug if RightScale::DevState.enabled?
    EM.threadpool_size = 1
    # Schedule boot sequence, don't run it now so agent is registered first
    EM.next_tick { init_boot } if RightScale::InstanceState.value == 'booting'
  end

  # Retrieve current instance state
  #
  # === Return
  # state<RightScale::OperationResult>:: Success operation result containing instance state
  def report_state
    state = RightScale::OperationResult.success(RightScale::InstanceState.value)
  end

  # Handle deconnection notification from broker
  # Start timer to give the amqp gem some time to retry connecting
  #
  # === Parameters
  # status<Symbol>:: Connection status, one of :connected or :deconnected
  #
  # === Return
  # true:: Always return true
  def connection_status(status)
    if status == :deconnected
      @offline_timer ||= EM::Timer.new(RECONNECT_GRACE_PERIOD) { RightScale::RequestForwarder.enable_offline_mode }
    else
      # Cancel offline timer if there was one
      if @offline_timer
        @offline_timer.cancel
        @offline_timer = nil
      end
      RightScale::RequestForwarder.disable_offline_mode
    end
    true
  end

  protected

  # We start off by setting the instance 'r_s_version' in the core site and
  # then proceed with the actual boot sequence
  #
  # === Return
  # true:: Always return true
  def init_boot
    request("/booter/set_r_s_version", { :agent_identity => @agent_identity, :r_s_version => 6 }) do |r|
      res = RightScale::OperationResult.from_results(r)
      strand("Failed to set_r_s_version", res) unless res.success?
      enable_managed_login
    end
    true
  end

  # Enable managed SSH for this instance, then continue with boot. Ensures that
  # managed SSH users can login to troubleshoot stranded and other 'interesting' events
  #
  # === Return
  # true:: Always return true
  def enable_managed_login
    request('/booter/get_login_policy', {:agent_identity => @agent_identity}) do |r|
      res = RightScale::OperationResult.from_results(r)

      if res.success?
        policy  = res.content
        auditor = RightScale::AuditorProxy.new(policy.audit_id)
        begin
          audit = RightScale::LoginManager.instance.update_policy(policy)
          auditor.create_new_section("Managed login enabled")
          auditor.append_info(audit)
        rescue Exception => e
          auditor.create_new_section('Failed to enable managed login')
          auditor.append_error("Error applying policy: #{e.message}")
          RightScale::RightLinkLog.error("#{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}")
        end
      else
        RightScale::RightLinkLog.error("Could not get login policy: #{res.content}")
      end

      boot
    end
  end

  # Retrieve software repositories and configure mirrors accordingly then proceed to
  # retrieving and running boot bundle.
  #
  # === Return
  # true:: Always return true
  def boot
    request("/booter/get_repositories", @agent_identity) do |r|
      res = RightScale::OperationResult.from_results(r)
      if res.success?
        reps = res.content.repositories
        @auditor = RightScale::AuditorProxy.new(res.content.audit_id)
        audit = "Using the following software repositories:\n"
        reps.each { |rep| audit += "  - #{rep.to_s}\n" }
        @auditor.create_new_section("Configuring software repositories")
        @auditor.append_info(audit)
        configure_repositories(reps)
        @auditor.update_status("Software repositories configured")
        @auditor.create_new_section('Preparing boot bundle')
        prepare_boot_bundle do |prep_res|
          if prep_res.success?
            @auditor.create_new_section('Boot bundle ready')
            run_boot_bundle(prep_res.content) do |boot_res|
              if boot_res.success?
                RightScale::InstanceState.value = 'operational'
              else
                strand("Failed to run boot sequence", boot_res)
              end
            end
          else
            strand("Failed to prepare boot bundle", prep_res)
          end
        end
      else
        strand("Failed to retrieve software repositories", res)
      end
    end
    true
  end

  # Log error to local log file and set instance state to stranded
  #
  # === Parameters
  # msg<String>:: Error message that will be audited and logged
  # res<RightScale::OperationResult>:: Operation result with additional information
  #
  # === Return
  # true:: Always return true
  def strand(msg, res)
    RightScale::InstanceState.value = 'stranded'
    msg += ": #{res.content}" if res.content<commit_msg>Update the status for bundle ready audit<commit_after>#
# Copyright (c) 2009 RightScale Inc
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

class InstanceSetup

  include Nanite::Actor

  expose :report_state

  # Number of seconds to wait before switching to offline mode
  RECONNECT_GRACE_PERIOD = 30

  # Boot if and only if instance state is 'booting'
  #
  # === Parameters
  # agent_identity<String>:: Serialized agent identity for current agent
  def initialize(agent_identity)
    @boot_retries = 0
    @agent_identity = agent_identity
    RightScale::InstanceState.init(agent_identity)
    RightScale::RightLinkLog.force_debug if RightScale::DevState.enabled?
    EM.threadpool_size = 1
    # Schedule boot sequence, don't run it now so agent is registered first
    EM.next_tick { init_boot } if RightScale::InstanceState.value == 'booting'
  end

  # Retrieve current instance state
  #
  # === Return
  # state<RightScale::OperationResult>:: Success operation result containing instance state
  def report_state
    state = RightScale::OperationResult.success(RightScale::InstanceState.value)
  end

  # Handle deconnection notification from broker
  # Start timer to give the amqp gem some time to retry connecting
  #
  # === Parameters
  # status<Symbol>:: Connection status, one of :connected or :deconnected
  #
  # === Return
  # true:: Always return true
  def connection_status(status)
    if status == :deconnected
      @offline_timer ||= EM::Timer.new(RECONNECT_GRACE_PERIOD) { RightScale::RequestForwarder.enable_offline_mode }
    else
      # Cancel offline timer if there was one
      if @offline_timer
        @offline_timer.cancel
        @offline_timer = nil
      end
      RightScale::RequestForwarder.disable_offline_mode
    end
    true
  end

  protected

  # We start off by setting the instance 'r_s_version' in the core site and
  # then proceed with the actual boot sequence
  #
  # === Return
  # true:: Always return true
  def init_boot
    request("/booter/set_r_s_version", { :agent_identity => @agent_identity, :r_s_version => 6 }) do |r|
      res = RightScale::OperationResult.from_results(r)
      strand("Failed to set_r_s_version", res) unless res.success?
      enable_managed_login
    end
    true
  end

  # Enable managed SSH for this instance, then continue with boot. Ensures that
  # managed SSH users can login to troubleshoot stranded and other 'interesting' events
  #
  # === Return
  # true:: Always return true
  def enable_managed_login
    request('/booter/get_login_policy', {:agent_identity => @agent_identity}) do |r|
      res = RightScale::OperationResult.from_results(r)

      if res.success?
        policy  = res.content
        auditor = RightScale::AuditorProxy.new(policy.audit_id)
        begin
          audit = RightScale::LoginManager.instance.update_policy(policy)
          auditor.create_new_section("Managed login enabled")
          auditor.append_info(audit)
        rescue Exception => e
          auditor.create_new_section('Failed to enable managed login')
          auditor.append_error("Error applying policy: #{e.message}")
          RightScale::RightLinkLog.error("#{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}")
        end
      else
        RightScale::RightLinkLog.error("Could not get login policy: #{res.content}")
      end

      boot
    end
  end

  # Retrieve software repositories and configure mirrors accordingly then proceed to
  # retrieving and running boot bundle.
  #
  # === Return
  # true:: Always return true
  def boot
    request("/booter/get_repositories", @agent_identity) do |r|
      res = RightScale::OperationResult.from_results(r)
      if res.success?
        reps = res.content.repositories
        @auditor = RightScale::AuditorProxy.new(res.content.audit_id)
        audit = "Using the following software repositories:\n"
        reps.each { |rep| audit += "  - #{rep.to_s}\n" }
        @auditor.create_new_section("Configuring software repositories")
        @auditor.append_info(audit)
        configure_repositories(reps)
        @auditor.update_status("Software repositories configured")
        @auditor.create_new_section('Preparing boot bundle')
        prepare_boot_bundle do |prep_res|
          if prep_res.success?
            @auditor.update_status('Boot bundle ready')
            run_boot_bundle(prep_res.content) do |boot_res|
              if boot_res.success?
                RightScale::InstanceState.value = 'operational'
              else
                strand("Failed to run boot sequence", boot_res)
              end
            end
          else
            strand("Failed to prepare boot bundle", prep_res)
          end
        end
      else
        strand("Failed to retrieve software repositories", res)
      end
    end
    true
  end

  # Log error to local log file and set instance state to stranded
  #
  # === Parameters
  # msg<String>:: Error message that will be audited and logged
  # res<RightScale::OperationResult>:: Operation result with additional information
  #
  # === Return
  # true:: Always return true
  def strand(msg, res)
    RightScale::InstanceState.value = 'stranded'
    msg += ": #{res.content}" if res.content
{'fraction_non_alphanumeric': 0.08250241235123834, 'fraction_numerical': 0.0014474107430041815, 'mean_word_length': 3.2989975803664016, 'pattern_counts': {'":': 2, '<': 13, '<?xml version=': 0, '>': 21, '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': '2200866', 'n_tokens_mistral': 3473, 'n_tokens_neox': 3332, 'n_words': 1149}