train-vmware 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +19 -0
- data/README.md +8 -0
- data/lib/train-vmware/connection.rb +175 -0
- data/lib/train-vmware/transport.rb +21 -0
- data/lib/train-vmware/version.rb +10 -0
- data/lib/train-vmware.rb +20 -0
- data/train-vmware.gemspec +45 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 238630d02bbca3ff3ba2bf7c27577bcb291222c3dbed50918601ac2100293989
|
4
|
+
data.tar.gz: 900a9ccf490ad20969bd1bda9e8cab0e309d44535df143cd665b8061bbd30448
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '09d46aaad75d424f1f3042627dc08cf4ba3b9205a97d72d2fedbe68663cccc7f7e75fa37836828059e6b6d5c49127c9944d235e56b7be4783f66d18198feb354'
|
7
|
+
data.tar.gz: 31f060e25871a77e487731566e490a2899f4c7f892395adbd7b582ae7d96ab3eb171e2a085c7da4ec9e1c4633573ca26eaf8edc4f9e2b7fba8d69a84d5856a18
|
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# This is Gemfile, which is used by bundler
|
4
|
+
# to ensure a coherent set of gems is installed.
|
5
|
+
# This file lists dependencies needed when outside
|
6
|
+
# of a gem (the gemspec lists deps for gem deployment)
|
7
|
+
|
8
|
+
# Bundler should refer to the gemspec for any dependencies.
|
9
|
+
gemspec
|
10
|
+
|
11
|
+
# Remaining group is only used for development.
|
12
|
+
group :development do
|
13
|
+
gem "bundler"
|
14
|
+
gem "byebug"
|
15
|
+
gem "inspec", ">= 2.2.112" # We need InSpec for the test harness while developing.
|
16
|
+
gem "minitest"
|
17
|
+
gem "rake"
|
18
|
+
gem "rubocop", "= 0.49.1" # Need to keep in sync with main InSpec project, so config files will work
|
19
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
# This allow us to inherit from Train::Plugins::Transport::BaseConnection
|
2
|
+
require "train"
|
3
|
+
require "open3"
|
4
|
+
require "ostruct" unless defined?(OpenStruct)
|
5
|
+
require "json" unless defined?(JSON)
|
6
|
+
require "mkmf"
|
7
|
+
|
8
|
+
module TrainPlugins
|
9
|
+
module VMware
|
10
|
+
# You must inherit from BaseConnection.
|
11
|
+
class Connection < Train::Plugins::Transport::BaseConnection
|
12
|
+
POWERSHELL_PROMPT_REGEX = /PS\s.*> $/.freeze
|
13
|
+
|
14
|
+
def initialize(options)
|
15
|
+
super(options)
|
16
|
+
|
17
|
+
options[:viserver] = options[:viserver] || options[:host]
|
18
|
+
options[:username] = options[:username] || options[:user]
|
19
|
+
|
20
|
+
@username = options[:username]
|
21
|
+
@viserver = options[:viserver]
|
22
|
+
@session = nil
|
23
|
+
@stdout_buffer = ""
|
24
|
+
@stderr_buffer = ""
|
25
|
+
|
26
|
+
@powershell_binary = detect_powershell_binary
|
27
|
+
|
28
|
+
if @powershell_binary == :powershell
|
29
|
+
require "train/transports/local"
|
30
|
+
@powershell = Train::Transports::Local::Connection.new(options)
|
31
|
+
end
|
32
|
+
|
33
|
+
if options[:insecure] == true
|
34
|
+
run_command_via_connection("Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$False")
|
35
|
+
end
|
36
|
+
|
37
|
+
@platform_details = {
|
38
|
+
release: "vmware-powercli-#{powercli_version}",
|
39
|
+
}
|
40
|
+
|
41
|
+
connect
|
42
|
+
end
|
43
|
+
|
44
|
+
def connect
|
45
|
+
login_command = "Connect-VIServer #{options[:viserver]} -User #{options[:username]} -Password #{options[:password]} | Out-Null"
|
46
|
+
result = run_command_via_connection(login_command)
|
47
|
+
|
48
|
+
if result.exit_status != 0
|
49
|
+
message = "Unable to connect to VIServer at #{options[:viserver]}. "
|
50
|
+
case result.stderr
|
51
|
+
when /Invalid server certificate/
|
52
|
+
message += "Certification verification failed. Please use `--insecure` or set `Set-PowerCLIConfiguration -InvalidCertificateAction Ignore` in PowerShell"
|
53
|
+
when /incorrect user name or password/
|
54
|
+
message += "Incorrect username or password"
|
55
|
+
else
|
56
|
+
message += result.stderr.gsub(/-Password .*\s/, "-Password REDACTED")
|
57
|
+
end
|
58
|
+
|
59
|
+
raise message
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def platform
|
64
|
+
force_platform!("vmware", @platform_details)
|
65
|
+
end
|
66
|
+
|
67
|
+
def run_command_via_connection(cmd, &_data_handler)
|
68
|
+
if @powershell_binary == :pwsh
|
69
|
+
result = parse_pwsh_output(cmd)
|
70
|
+
|
71
|
+
# Attach exit status to result
|
72
|
+
exit_status = parse_pwsh_output("echo $?").stdout.chomp
|
73
|
+
result.exit_status = exit_status == "True" ? 0 : 1
|
74
|
+
|
75
|
+
result
|
76
|
+
else
|
77
|
+
@powershell.run_command(cmd)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def unique_identifier
|
82
|
+
uuid_command = "(Get-VMHost | Get-View).hardware.systeminfo.uuid"
|
83
|
+
run_command_via_connection(uuid_command).stdout.chomp
|
84
|
+
end
|
85
|
+
|
86
|
+
def uri
|
87
|
+
"vmware://#{@username}@#{@viserver}"
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def detect_powershell_binary
|
93
|
+
if find_executable0("powershell")
|
94
|
+
:powershell
|
95
|
+
elsif find_executable0("pwsh")
|
96
|
+
:pwsh
|
97
|
+
else
|
98
|
+
raise "Cannot find PowerShell binary, is `pwsh` installed?"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Read from stdout pipe until prompt is received
|
103
|
+
def flush_stdout(pipe)
|
104
|
+
@stdout_buffer += pipe.read_nonblock(1) while @stdout_buffer !~ POWERSHELL_PROMPT_REGEX
|
105
|
+
@stdout_buffer
|
106
|
+
rescue IO::EAGAINWaitReadable
|
107
|
+
# We cannot know when the stdout pipe is finished so we keep reading
|
108
|
+
retry
|
109
|
+
ensure
|
110
|
+
@stdout_buffer = ""
|
111
|
+
end
|
112
|
+
|
113
|
+
# This must be called after `flush_stdout` to ensure buffer is full
|
114
|
+
def flush_stderr(pipe)
|
115
|
+
loop do
|
116
|
+
@stderr_buffer += pipe.read_nonblock(1)
|
117
|
+
end
|
118
|
+
rescue IO::EAGAINWaitReadable
|
119
|
+
# If `flush_stderr` is ran after reading stdout we know that all of
|
120
|
+
# stderr is in the pipe. Thus, we can return the buffer once the pipe
|
121
|
+
# is unreadable.
|
122
|
+
@stderr_buffer
|
123
|
+
ensure
|
124
|
+
@stderr_buffer = ""
|
125
|
+
end
|
126
|
+
|
127
|
+
def parse_pwsh_output(cmd)
|
128
|
+
session.stdin.puts(cmd)
|
129
|
+
|
130
|
+
stdout = flush_stdout(session.stdout)
|
131
|
+
|
132
|
+
# Remove stdin from stdout (including trailing newline)
|
133
|
+
stdout.slice!(0, cmd.length + 1)
|
134
|
+
|
135
|
+
# Remove prompt from stdout
|
136
|
+
stdout.gsub!(POWERSHELL_PROMPT_REGEX, "")
|
137
|
+
|
138
|
+
# Grab stderr
|
139
|
+
stderr = flush_stderr(session.stderr)
|
140
|
+
|
141
|
+
CommandResult.new(
|
142
|
+
stdout,
|
143
|
+
stderr,
|
144
|
+
nil # exit_status is attached in `run_command_via_connection`
|
145
|
+
)
|
146
|
+
end
|
147
|
+
|
148
|
+
def powercli_version
|
149
|
+
version_command = "[string](Get-Module -Name VMware.PowerCLI -ListAvailable | Select -ExpandProperty Version)"
|
150
|
+
result = run_command_via_connection(version_command)
|
151
|
+
if result.stdout.empty? || result.exit_status != 0
|
152
|
+
raise "Unable to determine PowerCLI Module version, is it installed?"
|
153
|
+
end
|
154
|
+
|
155
|
+
result.stdout.chomp
|
156
|
+
end
|
157
|
+
|
158
|
+
def session
|
159
|
+
return @session unless @session.nil?
|
160
|
+
|
161
|
+
stdin, stdout, stderr = Open3.popen3("pwsh")
|
162
|
+
|
163
|
+
# Remove leading prompt and intro text
|
164
|
+
flush_stdout(stdout)
|
165
|
+
|
166
|
+
@session = OpenStruct.new
|
167
|
+
@session.stdin = stdin
|
168
|
+
@session.stdout = stdout
|
169
|
+
@session.stderr = stderr
|
170
|
+
|
171
|
+
@session
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "train-vmware/connection"
|
2
|
+
|
3
|
+
# Train Plugins v1 are usually declared under the TrainPlugins namespace.
|
4
|
+
# Each plugin has three components: Transport, Connection, and (optionally) Platform.
|
5
|
+
# We'll only define the Transport here, but we'll refer to the others.
|
6
|
+
|
7
|
+
module TrainPlugins
|
8
|
+
module VMware
|
9
|
+
class VMware < Train.plugin(1)
|
10
|
+
name "vmware"
|
11
|
+
option :viserver, default: proc { ENV["VISERVER"] }
|
12
|
+
option :username, default: proc { ENV["VISERVER_USERNAME"] }
|
13
|
+
option :password, default: proc { ENV["VISERVER_PASSWORD"] }
|
14
|
+
option :insecure, default: false
|
15
|
+
|
16
|
+
def connection(_ = nil)
|
17
|
+
@connection ||= Connection.new(@options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# This file exists simply to record the version number of the plugin.
|
2
|
+
# It is kept in a separate file, so that your gemspec can load it and
|
3
|
+
# learn the current version without loading the whole plugin. Also,
|
4
|
+
# many CI servers can update this file when "version bumping".
|
5
|
+
|
6
|
+
module TrainPlugins
|
7
|
+
module VMware
|
8
|
+
VERSION = "0.1.4".freeze
|
9
|
+
end
|
10
|
+
end
|
data/lib/train-vmware.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# This file is known as the "entry point."
|
2
|
+
# This is the file Train will try to load if it
|
3
|
+
# thinks your plugin is needed.
|
4
|
+
|
5
|
+
# The *only* thing this file should do is setup the
|
6
|
+
# load path, then load plugin files.
|
7
|
+
|
8
|
+
# Next two lines simply add the path of the gem to the load path.
|
9
|
+
# This is not needed when being loaded as a gem; but when doing
|
10
|
+
# plugin development, you may need it. Either way, it's harmless.
|
11
|
+
libdir = File.dirname(__FILE__)
|
12
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
13
|
+
|
14
|
+
# It's traditional to keep your gem version in a separate file, so CI can find it easier.
|
15
|
+
require_relative "train-vmware/version"
|
16
|
+
|
17
|
+
# A train plugin has three components: Transport, Connection, and (optionally) Platform.
|
18
|
+
# Transport acts as the glue.
|
19
|
+
require_relative "train-vmware/transport"
|
20
|
+
require_relative "train-vmware/connection"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# As plugins are usually packaged and distributed as a RubyGem,
|
2
|
+
# we have to provide a .gemspec file, which controls the gembuild
|
3
|
+
# and publish process. This is a fairly generic gemspec.
|
4
|
+
|
5
|
+
# It is traditional in a gemspec to dynamically load the current version
|
6
|
+
# from a file in the source tree. The next three lines make that happen.
|
7
|
+
lib = File.expand_path("lib", __dir__)
|
8
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
9
|
+
require "train-vmware/version"
|
10
|
+
|
11
|
+
Gem::Specification.new do |spec|
|
12
|
+
# Importantly, all Train plugins must be prefixed with `train-`
|
13
|
+
spec.name = "train-vmware"
|
14
|
+
|
15
|
+
# It is polite to namespace your plugin under TrainPlugins::YourPluginInCamelCase
|
16
|
+
spec.version = TrainPlugins::VMware::VERSION
|
17
|
+
spec.authors = ["VMware"]
|
18
|
+
spec.email = ["none@none.com"]
|
19
|
+
spec.summary = "Train Plugin for VMware PowerCLI"
|
20
|
+
spec.description = "Plugin version of the native VMware Transport in InSpec Train"
|
21
|
+
spec.homepage = ""
|
22
|
+
spec.license = "Apache-2.0"
|
23
|
+
|
24
|
+
# Though complicated-looking, this is pretty standard for a gemspec.
|
25
|
+
# It just filters what will actually be packaged in the gem (leaving
|
26
|
+
# out tests, etc)
|
27
|
+
spec.files = %w{
|
28
|
+
README.md train-vmware.gemspec Gemfile
|
29
|
+
} + Dir.glob(
|
30
|
+
"lib/**/*", File::FNM_DOTMATCH
|
31
|
+
).reject { |f| File.directory?(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
# If you rely on any other gems, list them here with any constraints.
|
35
|
+
# This is how `inspec plugin install` is able to manage your dependencies.
|
36
|
+
# For example, perhaps you are writing a thing that talks to AWS, and you
|
37
|
+
# want to ensure you have `aws-sdk` in a certain version.
|
38
|
+
|
39
|
+
# If you only need certain gems during development or testing, list
|
40
|
+
# them in Gemfile, not here.
|
41
|
+
# Do not list inspec as a dependency of the train plugin.
|
42
|
+
|
43
|
+
# All plugins should mention train, > 1.4
|
44
|
+
spec.add_dependency "train", "~> 3.8"
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: train-vmware
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- VMware
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: train
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.8'
|
27
|
+
description: Plugin version of the native VMware Transport in InSpec Train
|
28
|
+
email:
|
29
|
+
- none@none.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- Gemfile
|
35
|
+
- README.md
|
36
|
+
- lib/train-vmware.rb
|
37
|
+
- lib/train-vmware/connection.rb
|
38
|
+
- lib/train-vmware/transport.rb
|
39
|
+
- lib/train-vmware/version.rb
|
40
|
+
- train-vmware.gemspec
|
41
|
+
homepage: ''
|
42
|
+
licenses:
|
43
|
+
- Apache-2.0
|
44
|
+
metadata: {}
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubygems_version: 3.2.32
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: Train Plugin for VMware PowerCLI
|
64
|
+
test_files: []
|