diff --git a/bin/vagrant b/bin/vagrant index 170527070..fbfdf58a3 100755 --- a/bin/vagrant +++ b/bin/vagrant @@ -55,6 +55,12 @@ if ARGV.include?("--color") opts[:ui_class] = Vagrant::UI::Colored end +# Highest precedence is if we have enabled machine-readable output +if ARGV.include?("--machine-readable") + ARGV.delete("--machine-readable") + opts[:ui_class] = Vagrant::UI::MachineReadable +end + # Default to colored output opts[:ui_class] ||= Vagrant::UI::Colored diff --git a/lib/vagrant/ui.rb b/lib/vagrant/ui.rb index 2e6d0fb8a..5f2cf6a6d 100644 --- a/lib/vagrant/ui.rb +++ b/lib/vagrant/ui.rb @@ -31,6 +31,14 @@ module Vagrant define_method(method) { |*args| } end + # For machine-readable output. + # + # @param [String] type The type of the data + # @param [Array] data The data associated with the type + def machine(type, *data) + @logger.info("Machine: #{type} #{data.inspect}") + end + # Returns a new UI class that is scoped to the given resource name. # Subclasses can then use this scope name to do whatever they please. # @@ -51,6 +59,46 @@ module Vagrant end end + class MachineReadable < Interface + include Util::SafePuts + + def initialize + super + + @lock = Mutex.new + end + + def ask(*args) + super + + # Machine-readable can't ask for input + raise Errors::UIExpectsTTY + end + + def machine(type, *data) + opts = {} + opts = data.pop if data.last.kind_of?(Hash) + + target = opts[:scope] || "" + + # Prepare the data by replacing characters that aren't outputted + data.each_index do |i| + data[i] = data[i].to_s + data[i].gsub!(",", "%!(VAGRANT_COMMA)") + data[i].gsub!("\n", "\\n") + data[i].gsub!("\r", "\\r") + end + + @lock.synchronize do + safe_puts("#{Time.now.utc.to_i},#{target},#{type},#{data.join(",")}") + end + end + + def scope(scope_name) + BasicScope.new(self, scope_name) + end + end + # This is a UI implementation that outputs the text as is. It # doesn't add any color. class Basic < Interface @@ -172,6 +220,14 @@ module Vagrant # By default do nothing, these aren't logged define_method(method) { |*args| @ui.send(method, *args) } end + + def machine(type, *data) + opts = {} + opts = data.pop if data.last.is_a?(Hash) + opts[:scope] = @scope + data << opts + @ui.machine(type, *data) + end end # This is a UI implementation that outputs color for various types diff --git a/plugins/commands/box/command/list.rb b/plugins/commands/box/command/list.rb index 94a4d3964..033c07d1c 100644 --- a/plugins/commands/box/command/list.rb +++ b/plugins/commands/box/command/list.rb @@ -30,6 +30,9 @@ module VagrantPlugins # and which don't, since we plan on doing that transparently. boxes.each do |name, provider, _v1| @env.ui.info("#{name.ljust(longest_box_length)} (#{provider})", :prefix => false) + + @env.ui.machine("box-name", name) + @env.ui.machine("box-provider", provider) end # Success, exit status 0 diff --git a/plugins/commands/status/command.rb b/plugins/commands/status/command.rb index 9dc5fb724..64145f19e 100644 --- a/plugins/commands/status/command.rb +++ b/plugins/commands/status/command.rb @@ -25,7 +25,15 @@ module VagrantPlugins results = [] with_target_vms(argv) do |machine| state = machine.state if !state - results << "#{machine.name.to_s.ljust(max_name_length)} #{machine.state.short_description} (#{machine.provider_name})" + current_state = machine.state + results << "#{machine.name.to_s.ljust(max_name_length)} " + + "#{current_state.short_description} (#{machine.provider_name})" + + opts = { scope: machine.name.to_s } + @env.ui.machine("provider-name", machine.provider_name, opts) + @env.ui.machine("state", current_state.id, opts) + @env.ui.machine("state-human-short", current_state.short_description, opts) + @env.ui.machine("state-human-long", current_state.long_description, opts) end message = nil diff --git a/website/docs/source/layouts/layout.erb b/website/docs/source/layouts/layout.erb index 180268387..ecb68dd04 100644 --- a/website/docs/source/layouts/layout.erb +++ b/website/docs/source/layouts/layout.erb @@ -119,6 +119,7 @@ >status >suspend >up + >Machine Readable Output <% end %> diff --git a/website/docs/source/stylesheets/_base.less b/website/docs/source/stylesheets/_base.less index 5ad3eacff..67bc8fea2 100644 --- a/website/docs/source/stylesheets/_base.less +++ b/website/docs/source/stylesheets/_base.less @@ -86,7 +86,7 @@ h6 { line-height: @font-size; } -p { +p, td { letter-spacing: 1px; line-height: 32px; diff --git a/website/docs/source/stylesheets/_components.less b/website/docs/source/stylesheets/_components.less index 5e84f3053..06d0564d6 100644 --- a/website/docs/source/stylesheets/_components.less +++ b/website/docs/source/stylesheets/_components.less @@ -1,74 +1,80 @@ /* compontents */ +table.mr-types { + th.mr-type { + width: 200px; + } +} + //search .search-bar { - border-bottom: 1px solid fade(@black, 10%); - position: relative; - z-index: 99999; //keep search above content - margin-bottom: -1px; + border-bottom: 1px solid fade(@black, 10%); + position: relative; + z-index: 99999; //keep search above content + margin-bottom: -1px; - .search-icon { - height: 60px; - width: 40px; - background: url(/images/search_icon.png) no-repeat 0 center; - } - - .search { - //.debug; - background: @white; - height: 60px; - - input, - button, - form { - .kill-effects; - border: none; - height: 60px; - font-size: 20px; - .museo-sans-light-italic; + .search-icon { + height: 60px; + width: 40px; + background: url(/images/search_icon.png) no-repeat 0 center; } - input { - margin:0; - padding: 0 0 0 0; - font-size: 20px; + .search { + //.debug; + background: @white; + height: 60px; - &:focus { + input, + button, + form { + .kill-effects; + border: none; + height: 60px; + font-size: 20px; + .museo-sans-light-italic; + } - } //focus - } + input { + margin:0; + padding: 0 0 0 0; + font-size: 20px; - button { - padding: 0 20px; - text-transform: uppercase; - } + &:focus { + + } //focus + } + + button { + padding: 0 20px; + text-transform: uppercase; + } - } //search + } //search } //search bar //pagination .pagination { - height: 80px; - background: @gray-background; - padding: 0; - margin:0 auto; - text-transform: uppercase; - color: @blue-text; - z-index: 999; - position:relative; + height: 80px; + background: @gray-background; + padding: 0; + margin:0 auto; + text-transform: uppercase; + color: @blue-text; + z-index: 999; + position:relative; - a.previous, - a.next { - padding: 25px 0; - } + a.previous, + a.next { + padding: 25px 0; + } - a.previous { - margin-left: 20px; - } + a.previous { + margin-left: 20px; + } - a.next { - margin-right: 20px; - } + a.next { + margin-right: 20px; + } } diff --git a/website/docs/source/v2/cli/machine-readable.html.md b/website/docs/source/v2/cli/machine-readable.html.md new file mode 100644 index 000000000..239273e9f --- /dev/null +++ b/website/docs/source/v2/cli/machine-readable.html.md @@ -0,0 +1,136 @@ +--- +page_title: "Machine Readable Output - Command-Line Interface" +sidebar_current: "cli-machinereadable" +--- + +# Machine Readable Output + +Every Vagrant commands accepts a `--machine-readable` flag which enables +machine readable output mode. In this mode, the output to the terminal +is replaced with machine-friendly output. + +This mode makes it easy to programmatically execute Vagrant and read data +out of it. This output format is protected by our +[backwards compatibility](/v2/installation/backwards-compatibility.html) +policy. Until Vagrant 2.0 is released, however, the machine readable output +may change as we determine more use cases for it. But the backwards +compatibility promise should make it safe to write client libraries to +parse the output format. + +
+

+Advanced topic! This is an advanced topic for use only if +you want to programmatically execute Vagrant. If you're just getting started +with Vagrant, you may safely skip this section. +

+
+ +## Work-In-Progress + +The machine-readable output is very new (released as part of Vagrant 1.4). +We're still gathering use cases for it and building up the output for each +of the commands. It is likely that what you may want to achieve with +the machine-readable output is not possible due to missing information. + +In this case, we ask that you please +[open an issue](https://github.com/mitchellh/vagrant/issues) +requesting that certain information become available. We'll most likely add +it! + +## Format + +The machine readable format is a line-oriented, comma-delimeted text format. +This makes it extremely easy to parse using standard Unix tools such as awk or +grep in addition to full programming languages like Ruby or Python. + +The format is: + +``` +timestamp,target,type,data... +``` + +Each component is explained below: + +* **timestamp** is a Unix timestamp in UTC of when the message was printed. + +* **target** is the target of the following output. This is empty if the + message is related to Vagrant globally. Otherwise, this is generally a machine + name so you can relate output to a specific machine when multi-VM is in use. + +* **type** is the type of machine-readable message being outputted. There are + a set of standard types which are covered later. + +* **data** is zero or more comma-seperated values associated with the prior + type. The exact amount and meaning of this data is type-dependent, so you + must read the documentation associated with the type to understand fully. + +Within the format, if data contains a comma, it is replaced with +`%!(VAGRANT_COMMA)`. This was preferred over an escape character such as \' +because it is more friendly to tools like awk. + +Newlines within the format are replaced with their respective standard escape +sequence. Newlines become a literal `\n` within the output. Carriage returns +become a literal `\r`. + +## Types + +This section documents all the available types that may be outputted +with the machine-readable output. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescription
box-name + Name of a box installed into Vagrant. +
box-provider + Provider for an installed box. +
provider-name + The provider name of the target machine. + targetted +
state + The state ID of the target machine. + targetted +
state-human-long + Human-readable description of the state of the machine. This is the + long version, and may be a paragraph or longer. + targetted +
state-human-short + Human-readable description of the state of the machine. This is the + short version, limited to at most a sentence. + targetted +