Class: TyrantManager

Inherits:
Object
  • Object
show all
Extended by:
Paths
Includes:
Paths, Util
Defined in:
lib/tyrant_manager/util.rb,
lib/tyrant_manager.rb,
lib/tyrant_manager/cli.rb,
lib/tyrant_manager/log.rb,
lib/tyrant_manager/paths.rb,
lib/tyrant_manager/runner.rb,
lib/tyrant_manager/command.rb,
lib/tyrant_manager/version.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-2011 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

MAGIC_LINE =
'Loquacious::Configuration.for( "manager" ) do'
Cli =
Main.create {
  author "Copyright (c) 2009-2011 Jeremy Hinegardner. All rights reserved"
  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.



162
163
164
165
166
167
168
169
# File 'lib/tyrant_manager.rb', line 162

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 TyrantManager home. #{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



65
66
67
68
69
# File 'lib/tyrant_manager.rb', line 65

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:



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

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 Tyrant Manager home directory found" unless dd
  return dd
end

.default_instances_dirObject



79
80
81
# File 'lib/tyrant_manager/paths.rb', line 79

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



116
117
118
119
120
121
122
123
124
# File 'lib/tyrant_manager.rb', line 116

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



76
77
78
79
80
# File 'lib/tyrant_manager.rb', line 76

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. And that config_file_basename file has the following line in it.

Loquacious::Configuration.for( "manager" ) do

Consider this a ‘magic line’ in the config file. If this line is in the config file then it is considered the top level tyrant root config file. This is done by line detection instead of evaluation since the configuration is not evaluated until later.

Returns:

  • (Boolean)


48
49
50
51
52
53
54
55
56
# File 'lib/tyrant_manager.rb', line 48

def is_tyrant_root?( dir )
  cfg = File.join( dir, config_file_basename )
  if File.directory?( dir ) and File.exist?( cfg ) then
    IO.readlines( cfg ).each do |line|
      return true if line.index( MAGIC_LINE )
    end
  end
  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



86
87
88
89
90
# File 'lib/tyrant_manager.rb', line 86

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



12
13
14
# File 'lib/tyrant_manager/log.rb', line 12

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.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/tyrant_manager.rb', line 131

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



178
179
180
# File 'lib/tyrant_manager.rb', line 178

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

#configurationObject

load the configuration



185
186
187
188
189
190
191
# File 'lib/tyrant_manager.rb', line 185

def configuration
  unless defined? @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.



235
236
237
238
239
240
241
242
243
244
# File 'lib/tyrant_manager.rb', line 235

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



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/tyrant_manager.rb', line 203

def instances
  unless defined? @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



171
172
173
# File 'lib/tyrant_manager.rb', line 171

def logger
  Logging::Logger[self]
end

#runner_for(options) ⇒ Object

Create a runner instance with the given options



196
197
198
# File 'lib/tyrant_manager.rb', line 196

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