Class: TyrantManager

Inherits:
Object
  • Object
show all
Extended by:
Paths
Includes:
Paths, Util
Defined in:
lib/tyrant_manager/version.rb,
lib/tyrant_manager.rb,
lib/tyrant_manager/cli.rb,
lib/tyrant_manager/log.rb,
lib/tyrant_manager/util.rb,
lib/tyrant_manager/paths.rb,
lib/tyrant_manager/runner.rb,
lib/tyrant_manager/command.rb,
lib/tyrant_manager/commands/list.rb,
lib/tyrant_manager/commands/stop.rb,
lib/tyrant_manager/commands/start.rb,
lib/tyrant_manager/commands/stats.rb,
lib/tyrant_manager/commands/status.rb,
lib/tyrant_manager/tyrant_instance.rb,
lib/tyrant_manager/commands/archive_ulogs.rb,
lib/tyrant_manager/commands/create_instance.rb,
lib/tyrant_manager/commands/replication_status.rb

Overview

– Copyright © 2009 Jeremy Hinegardner All rights reserved. See LICENSE and/or COPYING for details ++

Defined Under Namespace

Modules: Commands, Log, Paths, Util, Version Classes: Command, Error, Runner, TyrantInstance

Constant Summary collapse

Cli =
Main.create {
  author "Copyright 2009 (c) Jeremy Hinegardner"
  version ::TyrantManager::VERSION

  description <<-txt
  The command line tool for managing tyrant instances.

  Run 'tyrantmanager help modename' for more information
  txt

  run { help! }

  mode( :setup ) {
    description "Setup an tyrant manager location"
    argument( :home ) {
      description "The home directory of the tyrant manager"
      required
      default ::TyrantManager.default_or_home_directory
    }

    run { 
      TyrantManager::Log.init 
      TyrantManager.setup( params['home'].value ) 
    }
  }

  mode( 'create-instance' ) {
    description <<-txt
    Create a new tyrant instance in the specified directory
    txt

    argument( 'instance-home' ) do
      description <<-txt
      The home directory of the tyrant instance.  If this is a full path it 
      will be used.  If it is a relative path, it will be relative to the 
      manager's 'instances' configuration parameter
      txt
    end

    mixin :option_home
    mixin :option_log_level

    run { ::TyrantManager::Cli.run_command_with_params( "create-instance", params ) }
  }

  mode( 'start' ) {
    description "Start all the tyrants listed"
    mixin :option_home
    mixin :option_log_level
    mixin :argument_instances
    option( 'dry-run' ) {
      description "Do not start, just show the commands"
      default false
    }

    run { ::TyrantManager::Cli.run_command_with_params( 'start', params ) }
  }


  mode( 'stop' ) {
    description "Stop all the tyrants listed"
    mixin :option_home
    mixin :option_log_level
    mixin :argument_instances

    run { ::TyrantManager::Cli.run_command_with_params( 'stop', params ) }
  }

  mode('replication-status') {
    description "Describe the replication status of those servers using replication"
    mixin :option_home
    mixin :option_log_level
    mixin :argument_instances
    run { ::TyrantManager::Cli.run_command_with_params( 'replication-status', params ) }
  }

  mode('process-status') {
    description "Check the running status of all the tyrants listed"
    mixin :option_home
    mixin :option_log_level

    mixin :argument_instances

    run { ::TyrantManager::Cli.run_command_with_params( 'process-status', params ) }
  }

  mode( 'stats' ) {
    description "Dump the database statistics of each of the tyrants listed"
    mixin :option_home
    mixin :option_log_level

    mixin :argument_instances

    run { ::TyrantManager::Cli.run_command_with_params( 'stats', params ) }
  }


  mode('list') {
    description "list the instances and their home directories"
    mixin :option_home
    mixin :option_log_level
    mixin :argument_instances
    run { ::TyrantManager::Cli.run_command_with_params( 'list', params ) }
  }

  mode( 'archive-ulogs' ) {
    description "Archive the ulog files that are no longer necessary for replication"
    mixin :option_home
    mixin :option_log_level
    mixin :argument_instances

    option( 'archive-method' ) {
      description "The method of archiving, compress, or delete.  Choosing 'delete' will also delete previously 'compressed' ulog files."
      argument :required
      validate { |m| %w[ compress delete ].include?( m.downcase ) }
      default "compress"
    }

    option( 'dry-run' ) {
      description "Do not archive, just show the commands"
      default false
    }

    option( 'force' ) {
      description "Force a record through the master to the slave so the replication timestamps are updated."
      default false
      cast :boolean
    }

    option('force-key') {
      description "When used with --force, this is the key that is forced from the master to the slaves.
      The value iinserted at the key is the current timestamp in the form 
      #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
      argument :required
      default "__tyrant_manager.force_replication_at"
      cast :string
    }


    option( 'slaves' ) {
      description "Comma separated list of slave instances connection strings host:port,host:port,..."
      argument :required
      cast :list_of_string
    }

    run { ::TyrantManager::Cli.run_command_with_params( 'archive-ulogs', params ) }

  }


  #--- Mixins ---
  mixin :option_home do
    option( :home ) do
      description "The home directory of the tyrant manager"
      argument :required
      validate { |v| ::File.directory?( v ) }
      default ::TyrantManager.default_or_home_directory
    end
  end

  mixin :option_log_level do
    option('log-level') do
      description "The verbosity of logging, one of [ #{::Logging::LNAMES.map {|l| l.downcase}.join(", ")} ]"
      argument :required
      validate { |l| %w[ debug info warn error fatal off ].include?( l.downcase ) }
    end
  end

  mixin :argument_instances do
    argument('instances') do
      description "A comman separated list of instance names the tyrant manager  knows about"
      argument :required
      cast :list
      default 'all'
    end
  end
}
VERSION =
Version.to_s

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Paths

bin_path, data_path, home_dir, home_dir=, home_path, install_dir, install_path, instances_path, lib_path, log_path, spec_path, sub_path, tmp_path

Methods included from Util

#ip_of

Constructor Details

#initialize(directory = TyrantManager.default_directory) ⇒ TyrantManager

Initialize the manager, which is nothing more than creating the instance and setting the home directory.



148
149
150
151
152
153
154
155
# File 'lib/tyrant_manager.rb', line 148

def initialize( directory = TyrantManager.default_directory )
  self.home_dir = File.expand_path( directory ) 
  if File.exist?( self.config_file ) then
    configuration # force a load
  else
    raise Error, "#{home_dir} is not a valid archive. #{self.config_file} does not exist"
  end
end

Class Method Details

.basedirObject

The basename of the directory that holds the tyrant manager system



30
31
32
# File 'lib/tyrant_manager.rb', line 30

def basedir
  "tyrant"
end

.config_file_basenameObject

The basename of the default config file



23
24
25
# File 'lib/tyrant_manager.rb', line 23

def config_file_basename
  "config.rb"
end

.cwd_default_directoryObject

Return the path of the tyrant dir if there is one relative to the current working directory. This means that there is a config_file_basename in the current working directory.

returns Dir.pwd if this is the case, nil otherwise



51
52
53
54
55
# File 'lib/tyrant_manager.rb', line 51

def cwd_default_directory
  default_dir = Dir.pwd
  return default_dir if is_tyrant_root?( default_dir )
  return nil
end

.default_directoryObject

The default tyrant directory. It is the first of these that matches:

  • current directory if there is a config_file_basename file in the current dirctory

  • the value of the TYRANT_MANAGER_HOME environment variable

  • File.join( Config::CONFIG, basedir )

Raises:



86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/tyrant_manager.rb', line 86

def default_directory
  defaults = [ self.cwd_default_directory,
               self.env_default_directory,
               self.localstate_default_directory ]
  dd = nil
  loop do
    dd = defaults.shift
    break if dd or defaults.empty?
  end
  raise Error, "No default_directory found" unless dd
  return dd
end

.default_instances_dirObject



74
75
76
# File 'lib/tyrant_manager/paths.rb', line 74

def self.default_instances_dir
  self.home_path( "instances" )
end

.default_or_home_directoryObject

Return the default directory if it exists, otherwise fallback to .home_dir



102
103
104
105
106
107
108
109
110
# File 'lib/tyrant_manager.rb', line 102

def default_or_home_directory
  hd = TyrantManager.home_dir
  begin
    hd = TyrantManager.default_directory
  rescue => e
    # yup, using home
  end
  return hd
end

.env_default_directoryObject

Return the path of the tyrant dir as it pertains to the TYRANT_MANAGER_HOME environment variable. If the directory is a tyrant root, then return that directory, otherwise return nil



62
63
64
65
66
# File 'lib/tyrant_manager.rb', line 62

def env_default_directory
  default_dir = ENV['TYRANT_MANAGER_HOME']
  return default_dir if default_dir and is_tyrant_root?( default_dir )
  return nil
end

.is_tyrant_root?(dir) ⇒ Boolean

is the given directory a tyrant root directory. A tyrant root has a config_file_basename file in the top level

Returns:

  • (Boolean)


38
39
40
41
42
# File 'lib/tyrant_manager.rb', line 38

def is_tyrant_root?( dir )
  cfg = File.join( dir, config_file_basename )
  return true if File.directory?( dir ) and File.exist?( cfg )
  return false
end

.localstate_default_directoryObject

Return the path of the tyrant dir as it pertains to the default global setting of ‘lcoalstatedir’ which is /opt/local/var, /var, or similar



72
73
74
75
76
# File 'lib/tyrant_manager.rb', line 72

def localstate_default_directory
  default_dir = File.join( Config::CONFIG['localstatedir'], basedir )
  return default_dir if is_tyrant_root?( default_dir )
  return nil
end

.loggerObject



7
8
9
# File 'lib/tyrant_manager/log.rb', line 7

def self.logger
  ::Logging::Logger[self]
end

.setup(dir = default_directory) ⇒ Object

Setup the tyrant manager in the given directory. This means creating it if it does not exist.



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/tyrant_manager.rb', line 117

def setup( dir = default_directory )
  unless File.directory?( dir )
    logger.info "Creating directory #{dir}"
    FileUtils.mkdir_p( dir )
  end

  cfg = File.join( dir, config_file_basename )

  unless File.exist?( cfg )
    template = TyrantManager::Paths.data_path( config_file_basename )
    logger.info "Creating default config file #{cfg}"
    FileUtils.cp( template, dir )
  end

  %w[ instances log tmp ].each do |subdir|
    subdir = File.join( dir, subdir )
    unless File.directory?( subdir ) then
      logger.info "Creating directory #{subdir}"
      FileUtils.mkdir subdir 
    end
  end
  return TyrantManager.new( dir )
end

Instance Method Details

#config_fileObject

The configuration file for the manager



164
165
166
# File 'lib/tyrant_manager.rb', line 164

def config_file
  @config_file ||= File.join( home_dir, TyrantManager.config_file_basename ) 
end

#configurationObject

load the configuration



171
172
173
174
175
176
177
# File 'lib/tyrant_manager.rb', line 171

def configuration
  unless @configuration 
    eval( IO.read( self.config_file ) )
    @configuration = Loquacious::Configuration.for("manager")
  end
  return @configuration
end

#each_instance(limit = %w[ all ])) ⇒ Object

Yield each instance that exists in the limit list, if the limit exists.

If the limit does not exist then yield each instance.



221
222
223
224
225
226
227
228
229
230
# File 'lib/tyrant_manager.rb', line 221

def each_instance( limit = %w[ all ])
  limit = [ limit ].flatten
  every = (limit.first == "all")

  instances.keys.sort.each do |name|
    if every or limit.include?( name ) then
      yield instances[name]
    end
  end
end

#instancesObject

Return the list of instances that the manager knows about



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/tyrant_manager.rb', line 189

def instances
  unless @instances then
    candidates = [ self.instances_path ]
    if configuration.instances then
      candidates = configuration.instances
    end

    @instances = {}
    while not candidates.empty? do
      candidate = candidates.pop
      cpath = append_to_home_if_not_absolute( candidate )
      begin
        t = TyrantInstance.new( cpath )
        t.manager = self
        @instances[t.name] = t
      rescue TyrantManager::Error => e
        if File.directory?( cpath ) then
          Dir.glob( "#{cpath}/*" ).each do |epath|
            if File.directory?( epath ) then
              candidates.push epath
            end
          end
        end
      end
    end #while 
  end
  return @instances
end

#loggerObject



157
158
159
# File 'lib/tyrant_manager.rb', line 157

def logger
  Logging::Logger[self]
end

#runner_for(options) ⇒ Object

Create a runner instance with the given options



182
183
184
# File 'lib/tyrant_manager.rb', line 182

def runner_for( options )
  Runner.new( self, options )
end