168 lines
5.5 KiB
Ruby
168 lines
5.5 KiB
Ruby
require 'log4r'
|
|
|
|
require "vagrant/util/safe_puts"
|
|
|
|
module Vagrant
|
|
module Command
|
|
# Base class for any CLI commands.
|
|
#
|
|
# This class provides documentation on the interface as well as helper
|
|
# functions that a command has.
|
|
class Base
|
|
include Util::SafePuts
|
|
|
|
def initialize(argv, env)
|
|
@argv = argv
|
|
@env = env
|
|
@logger = Log4r::Logger.new("vagrant::command::#{self.class.to_s.downcase}")
|
|
end
|
|
|
|
# This is what is called on the class to actually execute it. Any
|
|
# subclasses should implement this method and do any option parsing
|
|
# and validation here.
|
|
def execute; end
|
|
|
|
protected
|
|
|
|
# Parses the options given an OptionParser instance.
|
|
#
|
|
# This is a convenience method that properly handles duping the
|
|
# originally argv array so that it is not destroyed.
|
|
#
|
|
# This method will also automatically detect "-h" and "--help"
|
|
# and print help. And if any invalid options are detected, the help
|
|
# will be printed, as well.
|
|
#
|
|
# If this method returns `nil`, then you should assume that help
|
|
# was printed and parsing failed.
|
|
def parse_options(opts=nil)
|
|
# Creating a shallow copy of the arguments so the OptionParser
|
|
# doesn't destroy the originals.
|
|
argv = @argv.dup
|
|
|
|
# Default opts to a blank optionparser if none is given
|
|
opts ||= OptionParser.new
|
|
|
|
# Add the help option, which must be on every command.
|
|
opts.on_tail("-h", "--help", "Print this help") do
|
|
safe_puts(opts.help)
|
|
return nil
|
|
end
|
|
|
|
opts.parse!(argv)
|
|
return argv
|
|
rescue OptionParser::InvalidOption
|
|
raise Errors::CLIInvalidOptions, :help => opts.help.chomp
|
|
end
|
|
|
|
# Yields a VM for each target VM for the command.
|
|
#
|
|
# This is a convenience method for easily implementing methods that
|
|
# take a target VM (in the case of multi-VM) or every VM if no
|
|
# specific VM name is specified.
|
|
#
|
|
# @param [String] name The name of the VM. Nil if every VM.
|
|
# @param [Boolean] single_target If true, then an exception will be
|
|
# raised if more than one target is found.
|
|
def with_target_vms(names=nil, options=nil)
|
|
# Using VMs requires a Vagrant environment to be properly setup
|
|
raise Errors::NoEnvironmentError if !@env.root_path
|
|
|
|
# Setup the options hash
|
|
options ||= {}
|
|
|
|
# Require that names be an array
|
|
names ||= []
|
|
names = [names] if !names.is_a?(Array)
|
|
|
|
# First determine the proper array of VMs.
|
|
vms = []
|
|
if names.length > 0
|
|
names.each do |name|
|
|
raise Errors::MultiVMEnvironmentRequired if !@env.multivm?
|
|
|
|
if name =~ /^\/(.+?)\/$/
|
|
# This is a regular expression name, so we convert to a regular
|
|
# expression and allow that sort of matching.
|
|
regex = Regexp.new($1.to_s)
|
|
|
|
@env.vms.each do |name, vm|
|
|
vms << vm if name =~ regex
|
|
end
|
|
|
|
raise Errors::VMNoMatchError if vms.empty?
|
|
else
|
|
# String name, just look for a specific VM
|
|
vms << @env.vms[name.to_sym]
|
|
raise Errors::VMNotFoundError, :name => name if !vms[0]
|
|
end
|
|
end
|
|
else
|
|
vms = @env.vms_ordered
|
|
end
|
|
|
|
# Make sure we're only working with one VM if single target
|
|
raise Errors::MultiVMTargetRequired if options[:single_target] && vms.length != 1
|
|
|
|
# If we asked for reversed ordering, then reverse it
|
|
vms.reverse! if options[:reverse]
|
|
|
|
# Go through each VM and yield it!
|
|
vms.each do |old_vm|
|
|
# We get a new VM from the environment here to avoid potentially
|
|
# stale VMs (if there was a config reload on the environment
|
|
# or something).
|
|
vm = @env.vms[old_vm.name]
|
|
yield vm
|
|
end
|
|
end
|
|
|
|
# This method will split the argv given into three parts: the
|
|
# flags to this command, the subcommand, and the flags to the
|
|
# subcommand. For example:
|
|
#
|
|
# -v status -h -v
|
|
#
|
|
# The above would yield 3 parts:
|
|
#
|
|
# ["-v"]
|
|
# "status"
|
|
# ["-h", "-v"]
|
|
#
|
|
# These parts are useful because the first is a list of arguments
|
|
# given to the current command, the second is a subcommand, and the
|
|
# third are the commands given to the subcommand.
|
|
#
|
|
# @return [Array] The three parts.
|
|
def split_main_and_subcommand(argv)
|
|
# Initialize return variables
|
|
main_args = nil
|
|
sub_command = nil
|
|
sub_args = []
|
|
|
|
# We split the arguments into two: One set containing any
|
|
# flags before a word, and then the rest. The rest are what
|
|
# get actually sent on to the subcommand.
|
|
argv.each_index do |i|
|
|
if !argv[i].start_with?("-")
|
|
# We found the beginning of the sub command. Split the
|
|
# args up.
|
|
main_args = argv[0, i]
|
|
sub_command = argv[i]
|
|
sub_args = argv[i + 1, argv.length - i + 1]
|
|
|
|
# Break so we don't find the next non flag and shift our
|
|
# main args.
|
|
break
|
|
end
|
|
end
|
|
|
|
# Handle the case that argv was empty or didn't contain any subcommand
|
|
main_args = argv.dup if main_args.nil?
|
|
|
|
return [main_args, sub_command, sub_args]
|
|
end
|
|
end
|
|
end
|
|
end
|