#!/usr/bin/env ruby ################################################################################ # # Author: Zachary Patten # Copyright: Copyright (c) Zachary Patten # License: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ################################################################################ require 'gli' require 'testlab' include GLI::App include TestLab::Utility::Misc version TestLab::VERSION program_desc %(TestLab - A toolkit for building virtual computer labs) program_long_desc <<-EOF TestLab is based around the abstraction of three main components: nodes, networks and containers. Nodes represent a system (bare-metal or virtualized) that hosts containers. Networks represent a Linux bridge on a node. Containers simply represent a Linux Container (LXC) running on its parent node which is typically connected to a network on the node. In addition to the core component abstractions, TestLab shares a series of core tasks that are universal across all of the components. These are create and destroy, up and down, provision and deprovision. Several other core tasks, such as build, demolish, recycle and bounce encapsulate the previously mentioned tasks and simply act as convenience tasks. You can execute almost all of the tasks against the entire lab, or individual lab components. When building a lab from scratch, you will typically run 'tl build'. To breakdown your lab, destroying all the components, you will typically run 'tl demolish'. If you want to re-build the lab you can run 'tl recycle' which will run the demolish task followed by the build task against all the lab components. If you want to power-cycle the entire lab you can run 'tl bounce' which will run the down task followed by the up task against all the lab components. Again these tasks can be run against the lab as a whole or individual components. You can view the status of the entire lab using 'tl status', or view the status of individual components using 'tl node status', 'tl network status' or 'tl container status'. You can easily get help for any of the component tasks using the syntax 'tl help '. This can be extended to the following syntax 'tl help ' or 'tl help ' for more in-depth help. EOF sort_help :manually default_command :help preserve_argv true require 'commands/support' require 'commands/testlab' require 'commands/node' require 'commands/network' require 'commands/container' # commands_from 'commands' desc 'Verbose output (or set VERBOSE=1 in your environment)' default_value false switch [:v, :verbose] desc 'Quiet output (or set QUIET=1 in your environment)' default_value false switch [:q, :quiet] desc 'Path to Labfile' arg_name 'path/to/file' flag [:l, :labfile] desc 'Path to Repository directory' arg_name 'path/to/directory' flag [:r, :repo] desc 'Path to Configuration directory' arg_name 'path/to/directory' flag [:c, :config] desc 'Path to Log file' default_value DEFAULT_LOG_FILE arg_name 'path/to/log_file' flag [:log] pre do |global,command,options,args| @testlab_start_time = Time.now.utc @verbose = ((ENV['VERBOSE'] == '1') || (global[:verbose] == true)) @quiet = ((ENV['QUIET'] == '1') || (global[:quiet] == true)) (@verbose == true) and (ENV['LOG_LEVEL'] = 'DEBUG') File.exists?(global[:log]) and FileUtils.move(global[:log], DEFAULT_LOG_BACKUP) logger = ZTK::Logger.new(global[:log]) if ((@verbose == true) || (@quiet == false)) logger.loggers << ::Logger.new(STDOUT) end @ui = ZTK::UI.new( :logger => logger, :verbose => @verbose, :quiet => @quiet ) @ui.logger.debug { "global(#{global.inspect})" } @ui.logger.debug { "options(#{options.inspect})" } @ui.logger.debug { "args(#{args.inspect})" } @testlab = TestLab.new( :ui => @ui, :labfile_path => global[:labfile], :config_dir => global[:config], :repo_dir => global[:repo] ) @testlab.boot if !@ui.quiet? message = format_message("TestLab v#{TestLab::VERSION} Loaded".black.bold) @ui.stdout.puts(message) @ui.logger.info { message } end true end post do |global,command,options,args| testlab_run_time = (Time.now.utc - @testlab_start_time) if !@ui.quiet? message = format_message("TestLab v#{TestLab::VERSION} Finished (%0.4f seconds)".black.bold % testlab_run_time) @ui.stdout.puts(message) @ui.logger.info { message } end File.exists?(DEFAULT_DUMP_FILE) and File.delete(DEFAULT_DUMP_FILE) true end on_error do |exception| @ui.stderr.puts(format_message(["ERROR:".red, exception.inspect.red.bold].join(' '))) case exception when GLI::BadCommandLine, GLI::UnknownCommand, GLI::UnknownCommandArgument, GLI::UnknownGlobalArgument then command_regex = /Command '([\w]+)' / command = exception.message.scan(command_regex).flatten.first @ui.stderr.puts commands[:help] and commands[:help].execute({}, {}, (command.nil? ? [] : [command.to_s])) false else testlab_run_time = (Time.now.utc - @testlab_start_time) dump_file = File.open(DEFAULT_DUMP_FILE, 'w') if !@testlab.nil? TestLab::Utility.log_header(@testlab).each { |line| dump_file.puts(line) } end dump_file.puts(exception.inspect) @ui.logger.fatal { exception.inspect } exception.backtrace.each do |line| dump_file.puts(line) @ui.logger << "#{line}\n" end message = format_message("TestLab v#{TestLab::VERSION} Aborted (%0.4f seconds)".black.bold % testlab_run_time) dump_file.puts(ZTK::ANSI.uncolor(message)) @ui.stderr.puts(message) @ui.logger.info { message } false end end exit run(ARGV)