kitchen-vcair 1.0.0
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/.gitignore +21 -0
- data/.rubocop.yml +14 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +201 -0
- data/README.md +188 -0
- data/examples/VIDEOS.md +57 -0
- data/examples/windows_customization.bat +53 -0
- data/kitchen-vcair.gemspec +32 -0
- data/lib/kitchen/driver/vcair.rb +359 -0
- data/lib/kitchen/driver/vcair_version.rb +25 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/vcair_spec.rb +825 -0
- metadata +167 -0
data/examples/VIDEOS.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# kitchen-vcair demonstration videos
|
|
2
|
+
|
|
3
|
+
Below are videos created by the original plugin author that demonstrate
|
|
4
|
+
kitchen-vcair and how to use it. Some configuration parameters have changed
|
|
5
|
+
since the initial development of this plugin to keep is consistent with other
|
|
6
|
+
VMware-based plugins, so be sure to check the README.md in this repo for the
|
|
7
|
+
latest instructions.
|
|
8
|
+
|
|
9
|
+
## Linux
|
|
10
|
+
|
|
11
|
+
* [github.com/vulk/kitchen-vcair](https://www.youtube.com/watch?v=5srDko69XJ0&t=03)
|
|
12
|
+
* [vchs.vmware.com](https://www.youtube.com/watch?v=5srDko69XJ0&t=15)
|
|
13
|
+
* [Walkthrough steps for cloning, building gem](https://www.youtube.com/watch?v=5srDko69XJ0&t=30)
|
|
14
|
+
* [git clone git@github.com:/vulk/kitchen-vcair.git](https://www.youtube.com/watch?v=5srDko69XJ0&t=68)
|
|
15
|
+
* [cd kitchen-vcair](https://www.youtube.com/watch?v=5srDko69XJ0&t=94)
|
|
16
|
+
* [gem build kitchen-vcair.gemspec](https://www.youtube.com/watch?v=5srDko69XJ0&t=100)
|
|
17
|
+
* [gem install ./kitchen-vcair-0.1.0.gem](https://www.youtube.com/watch?v=5srDko69XJ0&t=120)
|
|
18
|
+
* [quick look through code ](https://www.youtube.com/watch?v=5srDko69XJ0&t=126)
|
|
19
|
+
* [git clone git@github.com:chef-cookbooks/httpd.git ](https://www.youtube.com/watch?v=5srDko69XJ0&t=173)
|
|
20
|
+
* [walkthrough of .kitchen.vcair.yml](https://www.youtube.com/watch?v=5srDko69XJ0&t=199)
|
|
21
|
+
* [walkthrough of environment variables](https://www.youtube.com/watch?v=5srDko69XJ0&t=247)
|
|
22
|
+
* [kitchen test](https://www.youtube.com/watch?v=5srDko69XJ0&t=282)
|
|
23
|
+
* [vchs.vmware.com virtualmachine list, showing creation of helloworldtest VM](https://www.youtube.com/watch?v=5srDko69XJ0&t=296)
|
|
24
|
+
* [knife vcair server list showing creation of helloworld test VM](https://www.youtube.com/watch?v=5srDko69XJ0&t=326)
|
|
25
|
+
* [instance provisionied, waiting for ssh](https://www.youtube.com/watch?v=5srDko69XJ0&t=355)
|
|
26
|
+
* [ssh available, installing chef-client](https://www.youtube.com/watch?v=5srDko69XJ0&t=400)
|
|
27
|
+
* [chef-client starting](https://www.youtube.com/watch?v=5srDko69XJ0&t=499)
|
|
28
|
+
* [chef-client finished, apache install completed](https://www.youtube.com/watch?v=5srDko69XJ0&t=515)
|
|
29
|
+
* [Kitchen Setup and Verify](https://www.youtube.com/watch?v=5srDko69XJ0&t=516)
|
|
30
|
+
* [Kitichen Destroy](https://www.youtube.com/watch?v=5srDko69XJ0&t=517)
|
|
31
|
+
* [Kitchen is finished](https://www.youtube.com/watch?v=5srDko69XJ0&t=525)
|
|
32
|
+
* [vchs.vmware.com and knife vcair shows vm destroyed](https://www.youtube.com/watch?v=5srDko69XJ0&t=530)
|
|
33
|
+
|
|
34
|
+
## Windows
|
|
35
|
+
|
|
36
|
+
* [vmwair-vcair.env.example](https://www.youtube.com/watch?v=k8OZII4UGZs&t=09)
|
|
37
|
+
* [.kitchen.vcair.yml](https://www.youtube.com/watch?v=k8OZII4UGZs&t=20)
|
|
38
|
+
* [.yml / platforms:customization_script note](https://www.youtube.com/watch?v=k8OZII4UGZs&t=30)
|
|
39
|
+
* [customization_script install-winrm-vcair.bat](https://www.youtube.com/watch?v=k8OZII4UGZs&t=37)
|
|
40
|
+
* [git clone opscode-cookbooks/iis](https://www.youtube.com/watch?v=k8OZII4UGZs&t=54)
|
|
41
|
+
* [start coping files into iis cookbooks](https://www.youtube.com/watch?v=k8OZII4UGZs&t=60)
|
|
42
|
+
* [Add kitchen-vcair and kitchen-pester to the Gemfile](https://www.youtube.com/watch?v=k8OZII4UGZs&t=98)
|
|
43
|
+
* [bundle install kitchen vcair and pester](https://www.youtube.com/watch?v=k8OZII4UGZs&t=120)
|
|
44
|
+
* [KITCHEN_YAML=.kitchen.vcair.yml bundle exec kitchen verify](https://www.youtube.com/watch?v=k8OZII4UGZs&t=150)
|
|
45
|
+
* [Server is allocated.](https://www.youtube.com/watch?v=k8OZII4UGZs&t=270)
|
|
46
|
+
* ['pre'/'post' customization script ](https://www.youtube.com/watch?v=k8OZII4UGZs&t=300)
|
|
47
|
+
* ['pre' customization reboot ](https://www.youtube.com/watch?v=k8OZII4UGZs&t=412)
|
|
48
|
+
* ['post' customization boot ](https://www.youtube.com/watch?v=k8OZII4UGZs&t=440)
|
|
49
|
+
* [winrm is online](https://www.youtube.com/watch?v=k8OZII4UGZs&t=555)
|
|
50
|
+
* [installing chef omnibus](https://www.youtube.com/watch?v=k8OZII4UGZs&t=560)
|
|
51
|
+
* [chef-client starts](https://www.youtube.com/watch?v=k8OZII4UGZs&t=600)
|
|
52
|
+
* [iis:default recipe runs](https://www.youtube.com/watch?v=k8OZII4UGZs&t=630)
|
|
53
|
+
* [verification via kitche-pester](https://www.youtube.com/watch?v=k8OZII4UGZs&t=647)
|
|
54
|
+
* [kitchen verify complete!](https://www.youtube.com/watch?v=k8OZII4UGZs&t=660)
|
|
55
|
+
* [iis default web page via links](https://www.youtube.com/watch?v=k8OZII4UGZs&t=695)
|
|
56
|
+
* [kitchen verify again](https://www.youtube.com/watch?v=k8OZII4UGZs&t=710)
|
|
57
|
+
* [kitchen destroy](https://www.youtube.com/watch?v=k8OZII4UGZs&t=735)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
|
|
3
|
+
@rem First Boot...
|
|
4
|
+
if “%1%” == “precustomization” (
|
|
5
|
+
|
|
6
|
+
echo Do precustomization tasks
|
|
7
|
+
@rem during this boot the hostname is set, which requires a reboot
|
|
8
|
+
|
|
9
|
+
@rem we also enable winrm over http, plaintext, long timeout, more memory etc
|
|
10
|
+
|
|
11
|
+
cmd.exe /c winrm quickconfig -q
|
|
12
|
+
cmd.exe /c winrm quickconfig -transport:http
|
|
13
|
+
cmd.exe /c winrm set winrm/config @{MaxTimeoutms="1800000"}
|
|
14
|
+
cmd.exe /c winrm set winrm/config/winrs @{MaxMemoryPerShellMB="300"}
|
|
15
|
+
cmd.exe /c winrm set winrm/config/service @{AllowUnencrypted="true"}
|
|
16
|
+
cmd.exe /c winrm set winrm/config/service/auth @{Basic="true"}
|
|
17
|
+
cmd.exe /c winrm set winrm/config/client/auth @{Basic="true"}
|
|
18
|
+
cmd.exe /c winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"}
|
|
19
|
+
|
|
20
|
+
@rem Make sure winrm is off for this boot, but enabled on next
|
|
21
|
+
@rem as we don't want a tcp connection available until we are
|
|
22
|
+
@rem past postcustomization
|
|
23
|
+
|
|
24
|
+
cmd.exe /c net stop winrm
|
|
25
|
+
cmd.exe /c sc config winrm start= auto
|
|
26
|
+
|
|
27
|
+
@rem make sure the default on password age is unlimited
|
|
28
|
+
@rem this ensures we don't have a password change forced on us
|
|
29
|
+
cmd.exe /c net accounts /maxpwage:unlimited
|
|
30
|
+
|
|
31
|
+
@rem write out a timestamp for this first boot / customization completes
|
|
32
|
+
echo %DATE% %TIME% > C:\vm-is-customized
|
|
33
|
+
|
|
34
|
+
) else if “%1%” == “postcustomization” (
|
|
35
|
+
|
|
36
|
+
@rem Second Boot / start winrm, just incase, and fix firewall
|
|
37
|
+
|
|
38
|
+
cmd.exe /c net start winrm
|
|
39
|
+
cmd.exe /c netsh advfirewall firewall set rule group="remote administration" new enable=yes
|
|
40
|
+
cmd.exe /c netsh firewall add portopening TCP 5985 "Port 5985 for WinRM"
|
|
41
|
+
|
|
42
|
+
@rem Password Setting and Autologin currently seem broken
|
|
43
|
+
@rem when done via the API, so we MUST set it in the postcustomization phase
|
|
44
|
+
cmd.exe /c net user administrator Password1
|
|
45
|
+
|
|
46
|
+
@rem in some environments we found the need to specify a DNS address
|
|
47
|
+
@rem cmd.exe /c netsh interface ipv4 add dnsserver "Ethernet" address=8.8.8.8
|
|
48
|
+
@rem cmd.exe /c netsh interface ipv4 add dnsserver "Ethernet0" address=8.8.8.8
|
|
49
|
+
|
|
50
|
+
@rem this is our 'ready' boot, password and winrm should be up
|
|
51
|
+
echo %DATE% %TIME% > C:\vm-is-ready
|
|
52
|
+
|
|
53
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'kitchen/driver/vcair_version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'kitchen-vcair'
|
|
9
|
+
spec.version = Kitchen::Driver::VCAIR_VERSION
|
|
10
|
+
spec.authors = ['Chef Partner Engineering', 'Taylor Carpenter', 'Chris McClimans']
|
|
11
|
+
spec.email = %w(partnereng@chef.io wolfpack+c+t@vulk.co)
|
|
12
|
+
spec.description = 'A Test Kitchen vCloud Air driver'
|
|
13
|
+
spec.summary = 'A Test Kitchen vCloud Air driver built on Fog'
|
|
14
|
+
spec.homepage = 'https://github.com/chef-partners/kitchen-vcair'
|
|
15
|
+
spec.license = 'Apache 2.0'
|
|
16
|
+
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
20
|
+
spec.require_paths = %w(lib)
|
|
21
|
+
|
|
22
|
+
spec.required_ruby_version = '>= 2.0.0'
|
|
23
|
+
|
|
24
|
+
spec.add_dependency 'test-kitchen', '~> 1.4', '>= 1.4.1'
|
|
25
|
+
spec.add_dependency 'fog', '~> 1.33'
|
|
26
|
+
|
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
|
28
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
|
30
|
+
spec.add_development_dependency 'rubocop', '~> 0.33'
|
|
31
|
+
spec.add_development_dependency 'pry', '~> 0.10'
|
|
32
|
+
end
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# Encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Authors:: Chris McClimans (<c@vulk.co>)
|
|
4
|
+
# Authors:: Taylor Carpenter (<t@vulk.co>)
|
|
5
|
+
# Authors:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
6
|
+
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
|
7
|
+
# License:: Apache License, Version 2.0
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
|
|
21
|
+
require 'fog'
|
|
22
|
+
require 'kitchen'
|
|
23
|
+
require 'securerandom'
|
|
24
|
+
|
|
25
|
+
module Kitchen
|
|
26
|
+
module Driver
|
|
27
|
+
class Vcair < Kitchen::Driver::Base
|
|
28
|
+
attr_accessor :vapp_id
|
|
29
|
+
|
|
30
|
+
default_config :wait_for, 600
|
|
31
|
+
default_config :vcair_api_path, '/api'
|
|
32
|
+
default_config :catalog_id, nil
|
|
33
|
+
default_config :catalog_name, nil
|
|
34
|
+
default_config :image_id, nil
|
|
35
|
+
default_config :image_name, nil
|
|
36
|
+
default_config :vdc_id, nil
|
|
37
|
+
default_config :vdc_name, nil
|
|
38
|
+
default_config :network_id, nil
|
|
39
|
+
default_config :network_name, nil
|
|
40
|
+
default_config :cpus, 1
|
|
41
|
+
default_config :memory, 1024
|
|
42
|
+
default_config :vm_password
|
|
43
|
+
|
|
44
|
+
required_config :vcair_username
|
|
45
|
+
required_config :vcair_password
|
|
46
|
+
required_config :vcair_api_host
|
|
47
|
+
required_config :vcair_org
|
|
48
|
+
|
|
49
|
+
def initialize(config)
|
|
50
|
+
super
|
|
51
|
+
Fog.timeout = config[:wait_for].to_i
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def name
|
|
55
|
+
'vCloudAir'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def create(state)
|
|
59
|
+
return unless state[:vapp_id].nil?
|
|
60
|
+
|
|
61
|
+
validate!
|
|
62
|
+
|
|
63
|
+
create_server(state)
|
|
64
|
+
vm.wait_for { ready? }
|
|
65
|
+
state[:hostname] = vm.ip_address
|
|
66
|
+
|
|
67
|
+
info("Server #{state[:hostname]} is powered on. Waiting for it to be ready...")
|
|
68
|
+
wait_for_server(state)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def destroy(state)
|
|
72
|
+
return if state[:vapp_id].nil?
|
|
73
|
+
|
|
74
|
+
validate!
|
|
75
|
+
|
|
76
|
+
self.vapp_id = state[:vapp_id]
|
|
77
|
+
|
|
78
|
+
info("Destroying vApp #{vapp_id}...")
|
|
79
|
+
begin
|
|
80
|
+
vapp
|
|
81
|
+
rescue Fog::Compute::VcloudDirector::Forbidden
|
|
82
|
+
warn("Unable to locate vApp <#{state[:vapp_id]}> - assuming it is already destroyed.")
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
info('Powering off the vApp...')
|
|
87
|
+
vapp.power_off
|
|
88
|
+
|
|
89
|
+
info('Undeploying the vApp...')
|
|
90
|
+
vapp.undeploy
|
|
91
|
+
|
|
92
|
+
info('Deleting the vApp...')
|
|
93
|
+
vapp.destroy
|
|
94
|
+
|
|
95
|
+
info("vApp <#{state[:vapp_id]}> destroyed.")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def vcloud_client
|
|
99
|
+
@vcloud_client ||= Fog::Compute.new(fog_server_def)
|
|
100
|
+
rescue Excon::Errors::Unauthorized => e
|
|
101
|
+
raise "Connection failure, please check your username and password. -- #{e.message}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def org
|
|
105
|
+
@org ||= vcloud_client.organizations.get_by_name(config[:vcair_org])
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def create_server(state)
|
|
109
|
+
self.vapp_id = instantiate
|
|
110
|
+
state[:vapp_id] = vapp_id
|
|
111
|
+
|
|
112
|
+
info("vApp ID #{vapp_id} created.")
|
|
113
|
+
|
|
114
|
+
info('Validating the vApp...')
|
|
115
|
+
unless validate_vapp
|
|
116
|
+
destroy(state)
|
|
117
|
+
return
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
info('Updating the VM customization...')
|
|
121
|
+
update_customization
|
|
122
|
+
|
|
123
|
+
info('Adjusting VM hardware...')
|
|
124
|
+
adjust_hardware
|
|
125
|
+
|
|
126
|
+
info('Attaching it to the network...')
|
|
127
|
+
attach_network
|
|
128
|
+
|
|
129
|
+
info('Tagging the VM...')
|
|
130
|
+
tag_vm
|
|
131
|
+
|
|
132
|
+
info('Powering on the VM...')
|
|
133
|
+
power_on
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def adjust_hardware
|
|
137
|
+
vm.cpu = config[:cpus] if config[:cpus]
|
|
138
|
+
vm.memory = config[:memory] if config[:memory]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def attach_network_payload
|
|
142
|
+
{
|
|
143
|
+
PrimaryNetworkConnectionIndex: 0,
|
|
144
|
+
NetworkConnection: [
|
|
145
|
+
{
|
|
146
|
+
network: network.name,
|
|
147
|
+
needsCustomization: true,
|
|
148
|
+
NetworkConnectionIndex: 0,
|
|
149
|
+
IsConnected: true,
|
|
150
|
+
IpAddressAllocationMode: 'POOL'
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def attach_network
|
|
157
|
+
task = vcloud_client.put_network_connection_system_section_vapp(vm.id, attach_network_payload)
|
|
158
|
+
vcloud_client.process_task(task.body)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def tag_vm
|
|
162
|
+
vm.tags.create('created-by', 'test-kitchen')
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def power_on
|
|
166
|
+
vapp.power_on
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def wait_for_server(state)
|
|
170
|
+
instance.transport.connection(state).wait_until_ready
|
|
171
|
+
rescue
|
|
172
|
+
error("Server #{vapp.id} (#{vm.name}) not reachable. Destroying server...")
|
|
173
|
+
destroy(state)
|
|
174
|
+
raise
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def vcloud_username
|
|
178
|
+
[ config[:vcair_username], config[:vcair_org] ].join('@')
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def fog_server_def
|
|
182
|
+
{
|
|
183
|
+
provider: 'vclouddirector',
|
|
184
|
+
vcloud_director_username: vcloud_username,
|
|
185
|
+
vcloud_director_password: config[:vcair_password],
|
|
186
|
+
vcloud_director_host: config[:vcair_api_host],
|
|
187
|
+
vcloud_director_api_version: config[:vcair_api_version],
|
|
188
|
+
vcloud_director_show_progress: false,
|
|
189
|
+
path: config[:vcair_api_path]
|
|
190
|
+
}
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def image
|
|
194
|
+
if config[:image_id]
|
|
195
|
+
catalog.catalog_items.get(config[:image_id])
|
|
196
|
+
else
|
|
197
|
+
catalog.catalog_items.get_by_name(config[:image_name])
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def catalog
|
|
202
|
+
if config[:catalog_id]
|
|
203
|
+
org.catalogs.get(config[:catalog_id])
|
|
204
|
+
else
|
|
205
|
+
org.catalogs.get_by_name(config[:catalog_name])
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def vdc
|
|
210
|
+
if config[:vdc_id]
|
|
211
|
+
org.vdcs.get(config[:vdc_id])
|
|
212
|
+
else
|
|
213
|
+
org.vdcs.get_by_name(config[:vdc_name])
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def network
|
|
218
|
+
if config[:network_id]
|
|
219
|
+
org.networks.get(config[:network_id])
|
|
220
|
+
else
|
|
221
|
+
org.networks.get_by_name(config[:network_name])
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def node_description
|
|
226
|
+
config[:node_description] || "Test Kitchen: #{node_name}"
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def node_name
|
|
230
|
+
config[:node_name] || generate_node_name
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def generate_node_name
|
|
234
|
+
# SecureRandom.hex generates a string 2x the argument.
|
|
235
|
+
# We need the name to be 15 chars or less to play nicely
|
|
236
|
+
# with windows, so we're generating a 12-char random
|
|
237
|
+
# string prefixed with "tk-"
|
|
238
|
+
'tk-' + SecureRandom.hex(6)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def instantiate_config
|
|
242
|
+
{
|
|
243
|
+
vdc_id: vdc.id,
|
|
244
|
+
network_id: network.id,
|
|
245
|
+
description: node_description
|
|
246
|
+
}
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def print_error_and_exit(message)
|
|
250
|
+
error(message)
|
|
251
|
+
fail message
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def validate!
|
|
255
|
+
%w(vdc catalog image network).each do |param|
|
|
256
|
+
validate_parameter_pair!(param)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
[ :org, :vdc, :catalog, :image, :network].each do |method|
|
|
260
|
+
validate_method!(method)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
validate_customization_script!
|
|
264
|
+
validate_computer_name!
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def validate_parameter_pair!(param)
|
|
268
|
+
id_key = param + '_id'
|
|
269
|
+
name_key = param + '_name'
|
|
270
|
+
|
|
271
|
+
print_error_and_exit("No #{param} found. You must specify #{id_key} or #{name_key}.") if
|
|
272
|
+
config[id_key.to_sym].nil? && config[name_key.to_sym].nil?
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def validate_method!(method)
|
|
276
|
+
send(method)
|
|
277
|
+
rescue => e
|
|
278
|
+
raise "Unable to validate #{method} - check your configuration and try again. #{e.class} -- #{e.message}"
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def validate_computer_name!
|
|
282
|
+
# regex proudly modified after stealing from:
|
|
283
|
+
# http://stackoverflow.com/questions/2063213/regular-expression-for-validating-dns-label-host-name
|
|
284
|
+
print_error_and_exit('Node name is not valid - must be 15 characters or less, and be a valid Windows node name') unless
|
|
285
|
+
node_name =~ /^(?![0-9]+$)(?!-)[a-zA-Z0-9-]{,15}(?<!-)$/
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def validate_customization_script!
|
|
289
|
+
return unless config[:customization_script]
|
|
290
|
+
|
|
291
|
+
print_error_and_exit("Customization script #{config[:customization_script]} is not found or not readable.") unless
|
|
292
|
+
::File.readable?(config[:customization_script])
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def instantiate
|
|
296
|
+
image.instantiate(node_name, instantiate_config)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def vapp
|
|
300
|
+
@vapp ||= vdc.vapps.get(vapp_id)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def vm
|
|
304
|
+
@vm ||= vapp.vms.first
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def validate_vapp
|
|
308
|
+
vms = vapp.vms
|
|
309
|
+
if vms.empty?
|
|
310
|
+
error('vApp created, but did not contain any VMs')
|
|
311
|
+
return false
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
if vms.size > 1
|
|
315
|
+
error('vApp created, but contained more than one VM')
|
|
316
|
+
return false
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
true
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def customization
|
|
323
|
+
@customization ||= vm.customization
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def update_customization
|
|
327
|
+
set_customization_script if config[:customization_script]
|
|
328
|
+
set_customization_password
|
|
329
|
+
set_customization_computer_name
|
|
330
|
+
save_customization
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def set_customization_script
|
|
334
|
+
customization.script = ::File.read(config[:customization_script])
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def set_customization_password
|
|
338
|
+
if config[:vm_password]
|
|
339
|
+
customization.admin_password = config[:vm_password]
|
|
340
|
+
customization.admin_password_auto = false
|
|
341
|
+
customization.reset_password_required = false
|
|
342
|
+
else
|
|
343
|
+
customization.admin_password = nil
|
|
344
|
+
customization.admin_password_auto = true
|
|
345
|
+
customization.reset_password_required = false
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def set_customization_computer_name
|
|
350
|
+
customization.computer_name = node_name
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def save_customization
|
|
354
|
+
customization.enabled = true
|
|
355
|
+
customization.save
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
end
|