vcloud-box-spinner 0.2.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.
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ pkg/*
4
+ vendor/*
5
+ *.swp
6
+ *~
7
+ \#*
8
+ /Gemfile.lock
data/CHANGELOG ADDED
@@ -0,0 +1,8 @@
1
+ 0.2.0 2013-08-09
2
+
3
+ - Adding delete vApp feature
4
+ - Changed the syntax for the command line interface
5
+
6
+ 0.1.0 2013-08-07
7
+
8
+ - Introducing vcloud-box-spinner gem (Initial Version)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in vcloud-box-spinner.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2013 singhgarima
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20
+ OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # vCloud Provisioner
2
+
3
+ This is a wrapper around the vCloud director API that should allow for easy
4
+ provisioning of VMs.
5
+
6
+ ## Installation
7
+
8
+ gem install vcloud-box-spinner
9
+
10
+ ## Usage
11
+
12
+ You should be able to do `vcloud-box-spinner --help`
13
+
14
+ Usage: vcloud-box-spinner [options] <org_config> <machine_config>
15
+
16
+ Provision a machine described by the JSON template `machine_config` in the vCloud organisation
17
+ described in the JSON config file `org_config`
18
+
19
+ e.g. vcloud-box-spinner -u username orgs/staging.json machines/frontend-1.json
20
+
21
+ -u, --user=USERNAME vCloud username
22
+ -p, --password=PASSWORD vCloud password
23
+ -F, --ssh-config=FILENAME SSH config file(s) to use (can be specified multiple times)
24
+ -s, --setup-script=SETUP-SCRIPT path to setup script that should run after machine is brought up
25
+ -d, --debug Enable debugging output
26
+ -v, --verbose Enable verbose output
27
+ -h, --help Show usage instructions
28
+
29
+ To provision a machine you will need to specify at least two JSON files:
30
+
31
+ 1. A JSON config file which tells the provisioner about the vCloud
32
+ organisation into which it is to provision a vApp
33
+ 2. A JSON config file which defines the machine-specific setup
34
+
35
+ Options:
36
+
37
+ - `user` is the username on your "vmware vcloud director" page
38
+ (usually in the top right corner).
39
+ - `setup-script` allows you to pass a script file path (shell), which
40
+ would be loaded as guest customization script. The purpose of
41
+ providing this option, is to let user do some basic bootstraping.
42
+ The script is not for the purpose of encouraging configuration
43
+ management and that should be done separately. A particular example
44
+ of how you can use the script is - You can set ssh configuration for
45
+ a user(eg ci), which can ssh in the system later and run the config
46
+ management script/tool.
47
+ On how to write this script please refer the following links:
48
+
49
+ - [Understand Guest OS Customisation](http://pubs.vmware.com/vcd-51/index.jsp?topic=%2Fcom.vmware.vcloud.users.doc_51%2FGUID-BB682E4D-DCD7-4936-A665-0B0FBD6F0EB5.html)
50
+ - [Example of scripts](http://pubs.vmware.com/vcd-51/index.jsp?topic=%2Fcom.vmware.vcloud.users.doc_51%2FGUID-724EB7B5-5C97-4A2F-897F-B27F1D4226C7.html)
51
+
52
+ The best way to understand the formats of the json files, read the docs
53
+ [here](/docs/json_formats.md)
54
+
55
+ Once you have an org and machine config, you can invoke the provisioner as
56
+ follows:
57
+
58
+ vcloud-box-spinner -u username -p password org_config.json machine_config.json
59
+
60
+ ## Hacking
61
+
62
+ refer [here](/docs/hacking.md)
63
+
64
+ ### Testing
65
+
66
+ You can run the tests with:
67
+
68
+ bundle exec rake
69
+
70
+ ## Known Issues
71
+
72
+ - We are using fog ruby gem, as a wrapper around vCloud API. The fog gem currently hasn't released fix for "case
73
+ insensitivity for Set-Cookie headers"
74
+ [here](https://github.com/singhgarima/fog/commit/b7f8db97b15f1dc48541f5f2781f70fb2b743267). The fix till then is,
75
+ in your Gemfile, add the following lines
76
+
77
+ gem 'fog',
78
+ :git => 'git://github.com/fog/fog.git',
79
+ :ref => '3f034ccce030cff32e867f57482387d249b4da90'
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+ require 'rspec/core/rake_task'
4
+ require 'gem_publisher'
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |task|
7
+ task.pattern = FileList['spec/**/*_spec.rb']
8
+ end
9
+
10
+ task :default => :spec
11
+
12
+ desc "Publish gem to RubyGems.org"
13
+ task :publish_gem do |t|
14
+ gem = GemPublisher.publish_if_updated("vcloud-box-spinner.gemspec", :rubygems)
15
+ puts "Published #{gem}" if gem
16
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.dirname(__FILE__) + '/../lib'
4
+ require 'provisioner/cli'
5
+
6
+ $:.unshift File.dirname(__FILE__) + '/../lib'
7
+
8
+ Excon.defaults[:ssl_verify_peer] = false
9
+ EXCON_DEBUG = true
10
+
11
+ Provisioner::CLI.new(ARGV).execute
data/docs/hacking.md ADDED
@@ -0,0 +1,7 @@
1
+ # Contribute to this project
2
+
3
+ * To run tests
4
+
5
+ bundle exec rake spec
6
+
7
+ * In case you adding major changes bump up the version file `lib/provisioner/version.rb`
@@ -0,0 +1,65 @@
1
+ # Organisation & Machine JSON files
2
+
3
+ ## Tree structure
4
+
5
+ .
6
+ ├── machines
7
+ │ └── tester
8
+ │ ├── machine-1.json
9
+ │ └── machine-2.json
10
+ ├── orgs
11
+ ├── org1.json
12
+ └── org2.json
13
+
14
+ ## Organisation JSON file format
15
+
16
+ Each organisation JSON file contains key value pair to represent meta data for
17
+ the same. An example is as follows
18
+
19
+ {
20
+ "default": {
21
+ "template_name": "<catalog-item-name>",
22
+ "host": "<api-vendor-endpoint>",
23
+ "platform": "<platform-name>",
24
+ "organisation": "<org-name>",
25
+ "catalog_items": {
26
+ "<catalog-item-name": "https://api.vcd.portal.skyscapecloud.com/api/catalogItem/<catalog-item-uuid>"
27
+ }
28
+ },
29
+ "<vdc-ref-name/zone>": {
30
+ "default_vdc": "https://api.vcd.portal.skyscapecloud.com/api/vdc/<vdc-uuid>",
31
+ "network_name": "<network-name>",
32
+ "network_uri": "https://api.vcd.portal.skyscapecloud.com/api/network/<network-uuid>",
33
+ "vdc_id": "<vdc-uuid>"
34
+ }
35
+ }
36
+
37
+ * catalog is a terminology used by vcloud to represent templates used as base
38
+ images
39
+ * org-name is the name of organisation on which you would be bringing up the
40
+ machines.
41
+ * vdc-ref-name/zone, is an non vcloud specific term, which we use to map the
42
+ machines to a particular network. You would see the reference to the value
43
+ in machine JSON file.
44
+ * default_vdc, is the herf to vdc network.
45
+ * network-name, can be found out from vcloud UI `Adminstration -> Your VDC -> Org VDC Networks -> the network you would use it for`
46
+
47
+ To find various uuids, please refer [here](/docs/uuids.md)
48
+
49
+ Note: We would be removing platform from the settings, as it is currently only
50
+ used to set facter variables on the newly set up machine.
51
+
52
+ ## Machine JSON file format
53
+
54
+ Each machine JSON file contain key value pair to represent machine specific
55
+ meta data
56
+
57
+ {
58
+ "zone": "<vdc-ref-name/zone>",
59
+ "vm_name": "vm-machine-name",
60
+ "ip": "<internal-ip-addr>"
61
+ }
62
+
63
+ * zone, is the reference to <vdc-ref-name/zone> used in organisation json
64
+ * vm-machine-name, is the name of vApp
65
+ * ip is the internal ip addr that would be used.
data/docs/uuids.md ADDED
@@ -0,0 +1,41 @@
1
+ # Finding various UUIDs needed by the JSON files
2
+
3
+ ## Catalog Item UUID
4
+
5
+ * Authorization:
6
+
7
+ curl -v -X POST -d '' -H "Accept: application/*+xml;version=5.1" -u "username@org-name:password" "https://vendor-api-endpoint/api/sessions"
8
+
9
+ - The above returns x-vcloud-authorization token
10
+ - The abpve request returns xml, which contains the reference to the organisation details url, similar to below
11
+
12
+ <Link rel="down" type="application/vnd.vmware.vcloud.org+xml" name="org-name" href="https://vendor-api-endpoint/api/org/{org-uuid}"/>
13
+
14
+ * Get org details:
15
+
16
+ curl -v --insecure -H "x-vcloud-authorization: <token>" -H "Accept: application/*+xml;version=5.1" https://vendor-api-endpoint/api/org/{org-uuid}
17
+
18
+ - The above returns catalog details. (In vcloud you have a catalog which is a group pf catalogItems i.e templates)
19
+
20
+ <Link rel="down" type="application/vnd.vmware.vcloud.catalog+xml" name="Default" href="https://vendor-api-endpoint/api/catalog/{catalog-uuid}"/>
21
+
22
+ * Get catalog items
23
+
24
+ curl -v --insecure -H "x-vcloud-authorization: {token}" -H "Accept: application/*+xml;version=5.1" https://vendor-api-endpoint/api/catalog/{catalog-uuid}
25
+
26
+ - The above returns all the catalogItems in the catalog, you can choose the desired one
27
+
28
+ <CatalogItem type="application/vnd.vmware.vcloud.catalogItem+xml" name="{catalog-item-name}" href="https://vendor-api-endpoint/api/catalogItem/{catalog-item-uuid}"/>
29
+
30
+
31
+ ## Network UUID
32
+
33
+ * Authorization: (same as describe in above steps)
34
+
35
+ * Get org details:
36
+
37
+ curl -v --insecure -H "x-vcloud-authorization: <token>" -H "Accept: application/*+xml;version=5.1" https://vendor-api-endpoint/api/org/{org-uuid}
38
+
39
+ - The above returns network details, choose the one you need on basis of name
40
+
41
+ <Link rel="down" type="application/vnd.vmware.vcloud.orgNetwork+xml" name="{network-name}" href="https://vendor-api-endpoint/api/network/{network-uuid}"/>
data/jenkins.sh ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/bash -x
2
+ set -e
3
+ bundle install --path "${HOME}/bundles/${JOB_NAME}"
4
+ bundle exec rake spec
5
+ bundle exec rake publish_gem --trace
@@ -0,0 +1,16 @@
1
+ require 'fog/vcloud/models/compute/server'
2
+ module Fog
3
+ module Vcloud
4
+ class Compute
5
+ class Server < Fog::Vcloud::Model
6
+ def ready?
7
+ reload_status
8
+ running_tasks = tasks && tasks.flatten.any? do |task|
9
+ task.kind_of?(Hash) && (task[:status] == 'running' && task[:Progress] != '100')
10
+ end
11
+ status != '0' && !running_tasks
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ require 'builder'
2
+ require 'fog/vcloud/compute'
3
+ require 'fog/vcloud/requests/compute/instantiate_vapp_template'
4
+
5
+ module Fog::Vcloud::Compute::Shared
6
+ private
7
+ def generate_instantiate_vapp_template_request(options)
8
+ xml = ::Builder::XmlMarkup.new
9
+ xml.InstantiateVAppTemplateParams(xmlns.merge!(:name => options[:name], :"xml:lang" => "en")) {
10
+ xml.Description(options[:description])
11
+ xml.InstantiationParams {
12
+ if options[:network_uri]
13
+ # TODO - implement properly
14
+ xml.NetworkConfigSection {
15
+ xml.ovf :Info
16
+ xml.NetworkConfig(:networkName => options[:network_name]) {
17
+ xml.Configuration {
18
+ xml.ParentNetwork(:href => options[:network_uri])
19
+ xml.FenceMode 'bridged'
20
+ }
21
+ }
22
+ }
23
+ end
24
+ }
25
+ # The template
26
+ xml.Source(:href => options[:template_uri])
27
+ xml.AllEULAsAccepted("true")
28
+ }
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ module Provisioner
2
+ class BlankProvisioner < Provisioner
3
+ include ComputeNode
4
+ end
5
+ end
@@ -0,0 +1,151 @@
1
+ require 'json'
2
+ require 'optparse'
3
+ require 'provisioner/errors'
4
+ require 'highline/import'
5
+ require 'vcloud_box_provisioner'
6
+
7
+ module Provisioner
8
+ class CLI
9
+ def self.defaults
10
+ {
11
+ :debug => false,
12
+ :log_level => 5,
13
+ :memory => 4096,
14
+ :num_cores => 2,
15
+ :num_servers => 1,
16
+ :platform => "production",
17
+ :ssh_config => true, # if not specified, use system defaults
18
+ }.freeze
19
+ end
20
+
21
+ def self.process(options = {})
22
+ # The template must specify a zone so we know where to look in the
23
+ # organisation config
24
+ begin
25
+ zone = options[:machine_metadata].fetch(:zone)
26
+ rescue KeyError
27
+ raise ConfigurationError, "The machine configuration doesn't specify " +
28
+ "a zone (Maybe you've put machine metadata and org config in the wrong order?)"
29
+ end
30
+
31
+ # Internal defaults
32
+ res = self.defaults.dup
33
+ # vCloud config defaults
34
+ org_config = options.delete(:org_config)
35
+ res.merge!(org_config.fetch(:default, {}))
36
+ # vCloud zone defaults
37
+ res.merge!(org_config.fetch(zone.to_sym, {}))
38
+ # Machine metadata options
39
+ res.merge!(options.delete(:machine_metadata))
40
+ # Command line options
41
+ res.merge!(options)
42
+
43
+ unless res.include? :catalog_id
44
+ begin
45
+ template_name = res.fetch(:template_name)
46
+ catalog_id = res.fetch(:catalog_items).fetch(template_name.to_sym)
47
+ rescue KeyError
48
+ raise ConfigurationError, 'You must specify catalog_id OR (catalog_items AND template_name)'
49
+ end
50
+ res[:catalog_id] = catalog_id
51
+ end
52
+
53
+ res
54
+ end
55
+
56
+
57
+ def initialize( args )
58
+ @args = args
59
+ end
60
+
61
+ def execute
62
+ options = {}
63
+
64
+ optparser = OptionParser.new do |o|
65
+
66
+ o.banner = "Usage: #{File.basename($0)} [options] <action>"
67
+
68
+ o.separator ""
69
+ o.separator "Provision a machine described by the JSON template `machine_metadata` in the vCloud organisation"
70
+ o.separator "described in the JSON config file `org_config`"
71
+ o.separator ""
72
+ o.separator "e.g. vcloud-box-spinner -u johndoe -o orgs/staging.json -m machines/frontend-1.json create"
73
+ o.separator ""
74
+ o.separator "[Available actions]:"
75
+ o.separator " #{Provisioner::AVAILABLE_ACTIONS.join(', ')}"
76
+ o.separator ""
77
+ o.separator "[Available options]:"
78
+
79
+ o.on("-u", "--user", "=USERNAME", "vCloud username") do |v|
80
+ options[:user] = v
81
+ end
82
+
83
+ o.on("-p", "--password", "=PASSWORD", "vCloud password") do |v|
84
+ options[:password] = v
85
+ end
86
+
87
+ o.on("-F", "--ssh-config", "=FILENAME", "SSH config file(s) to use (can be specified multiple times)") do |v|
88
+ options[:ssh_config] ||= []
89
+ options[:ssh_config].push(v)
90
+ end
91
+
92
+ options[:org_config] = {}
93
+ o.on("-o", "--org-config", "=ORG-CONFIG-JSON",
94
+ "The organisation configuration json file path") do |v|
95
+ options[:org_config] = JSON.parse(File.read(v), :symbolize_names => true)
96
+ end
97
+
98
+ options[:machine_metadata] = {}
99
+ o.on("-m", "--machine-config", "=METADATA",
100
+ "The machine configuration json file path") do |v|
101
+ options[:machine_metadata] = JSON.parse(File.read(v), :symbolize_names => true)
102
+ end
103
+
104
+ o.on('-s', '--setup-script', "=SETUP-SCRIPT", "path to setup script that should run after machine is brought up") do |v|
105
+ options[:setup_script] = v
106
+ end
107
+
108
+ o.on("-d", "--debug", "Enable debugging output") do
109
+ options[:debug] = true
110
+ end
111
+
112
+ o.on("-v", "--verbose", "Enable verbose output") do
113
+ options[:log_level] = 0
114
+ end
115
+
116
+ o.on_tail("-h", "--help", "Show usage instructions") do
117
+ puts o
118
+ exit
119
+ end
120
+ end
121
+
122
+ begin
123
+ optparser.parse!(@args)
124
+
125
+ if @args.length != 1
126
+ raise ConfigurationError, "#{File.basename($0)} takes one argument"
127
+ end
128
+
129
+ action = @args[0]
130
+
131
+ if options[:user].nil? then
132
+ options[:user] = ask("vCloud username: ")
133
+ end
134
+
135
+ if options[:password].nil? then
136
+ options[:password] = ask("vCloud password: ") { |q| q.echo = false }
137
+ end
138
+
139
+ provisioner_opts = self.class.process(options)
140
+
141
+ provisioner = VcloudBoxProvisioner.build provisioner_opts
142
+ provisioner.execute(action)
143
+ rescue OptionParser::InvalidArgument, ConfigurationError => e
144
+ $stderr.puts "Error: #{e}"
145
+ $stderr.puts
146
+ $stderr.puts optparser
147
+ exit 1
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,176 @@
1
+ module Provisioner
2
+ module ComputeAction
3
+ module Create
4
+ def user_data_files
5
+ [File.expand_path(options[:setup_script])]
6
+ end
7
+ private :user_data_files
8
+
9
+ def user_data
10
+ user_data_files.map { |f|
11
+ File.read(f)
12
+ }.join("\n")
13
+ end
14
+ private :user_data
15
+
16
+ def provision(name, options)
17
+ logger.debug "User data for #{name}: #{user_data}"
18
+ server = compute.servers.create(
19
+ :vdc_uri => options[:vdc_id],
20
+ :catalog_item_uri => options[:catalog_id],
21
+ :name => options[:vm_name],
22
+ :network_uri => options[:network_uri],
23
+ :network_name => options[:network_name],
24
+ :user_data => user_data,
25
+ :connection_options => {
26
+ :ssl_verify_peer => false,
27
+ :omit_default_port => true
28
+ }
29
+ )
30
+ notify "Waiting for server to come up", name
31
+ server.wait_for { server.ready? }
32
+ server
33
+ end
34
+ private :provision
35
+
36
+ def modify_xml(url, mime_type)
37
+ connection = compute.servers.service
38
+ xml = Nokogiri::XML(connection.request(:uri => url).body)
39
+ yield xml
40
+ connection.request(:uri => url,
41
+ :expects => 202,
42
+ :method => 'PUT',
43
+ :body => xml.to_s,
44
+ :headers => {'Content-Type' => mime_type})
45
+ end
46
+ private :modify_xml
47
+
48
+ def update_guest_customization_options(server, options)
49
+ logger.debug "server attributes: #{server.attributes}"
50
+ customization_options = compute.servers.service.get_customization_options(server.attributes[:children][:href]).body
51
+ guest_customization_section = customization_options[:GuestCustomizationSection]
52
+
53
+ response = modify_xml(guest_customization_section[:href], guest_customization_section[:type]) do |xml|
54
+ xml.at_css('ComputerName').content = options[:vm_name]
55
+ if xml.at_css('CustomizationScript').nil?
56
+ xml.at_css('ComputerName').before("<CustomizationScript>#{CGI.escapeHTML(user_data)}</CustomizationScript>\n")
57
+ else
58
+ xml.at_css('CustomizationScript').content = user_data
59
+ end
60
+ logger.debug(xml.to_xml)
61
+ end
62
+
63
+ wait_for_task(server, extract_task_uri(response))
64
+ server.wait_for { server.ready? }
65
+ end
66
+ private :update_guest_customization_options
67
+
68
+ def update_machine_resources(server, options)
69
+ update(server, "cpu", options[:num_cores])
70
+ update(server, "memory", options[:memory])
71
+
72
+ server.wait_for { server.ready? }
73
+ end
74
+ private :update_machine_resources
75
+
76
+ def update(server, resource_type, value)
77
+ virtual_hardware_section_links = server.attributes[:children]['ovf:VirtualHardwareSection'.to_sym][:Link]
78
+ edit_link = virtual_hardware_section_links.select { |item| item[:href].include?(resource_type) && item[:rel] == "edit" }.first
79
+
80
+ response = modify_xml(edit_link[:href], edit_link[:type]) do |xml|
81
+ xml.at_xpath('//rasd:VirtualQuantity').content = value
82
+ end
83
+
84
+ wait_for_task(server, extract_task_uri(response))
85
+ end
86
+ private :update
87
+
88
+ def extract_task_uri(response)
89
+ response_xml = Nokogiri::XML(response.body)
90
+ response_xml.at_css("Task").attributes['href'].value
91
+ end
92
+ private :extract_task_uri
93
+
94
+ def wait_for_task(server, task_uri)
95
+ connection = compute.servers.service
96
+
97
+ server.wait_for do
98
+ puts "... "
99
+ task = Nokogiri::XML(connection.request(:uri => task_uri, :expects => 200).body)
100
+ task.at_css("Task").attributes['status'].value == 'success'
101
+ end
102
+ end
103
+ private :wait_for_task
104
+
105
+ def update_network_connection_options(server, options)
106
+ network_connection_section = server.attributes[:children][:NetworkConnectionSection][:Link]
107
+ logger.debug "server attributes: #{server.attributes}"
108
+ logger.debug "NetworkConnectionSection #{network_connection_section}"
109
+
110
+ response = modify_xml(network_connection_section[:href], network_connection_section[:type]) do |xml|
111
+ if options[:ip]
112
+ if xml.at_css('IpAddress').nil?
113
+ xml.at_css('IsConnected').before("<IpAddress>#{options[:ip]}</IpAddress>\n")
114
+ else
115
+ xml.at_css('IpAddress').content = options[:ip]
116
+ end
117
+ xml.at_css('IpAddressAllocationMode').content = 'MANUAL'
118
+ else
119
+ xml.at_css('IpAddressAllocationMode').content = 'POOL'
120
+ end
121
+ xml.at_css('NetworkConnection')[:network] = options[:network_name]
122
+ xml.at_css('IsConnected').content = 'true'
123
+ end
124
+
125
+ wait_for_task(server, extract_task_uri(response))
126
+ server.wait_for { server.ready? }
127
+ end
128
+ private :update_network_connection_options
129
+
130
+ def power_on(server)
131
+ connection = compute.servers.service
132
+ power_on_uri = server.links.find {|link| link[:rel] == 'power:powerOn' }[:href]
133
+ connection.request(:uri => power_on_uri, :method => "POST", :expects => 202)
134
+ end
135
+ private :power_on
136
+
137
+ def wait_for_vms_to_appear(server, options)
138
+ 100.times.each do |x|
139
+ logger.debug("waiting for vm to spin up...")
140
+ if server.attributes[:children] && server.attributes[:children][:href]
141
+ return
142
+ end
143
+ sleep 1
144
+ end
145
+
146
+ if !server.attributes[:children] || !server.attributes[:children][:href]
147
+ abort "vm didn't properly spin up"
148
+ end
149
+ end
150
+ private :wait_for_vms_to_appear
151
+
152
+ def launch_server name
153
+ super
154
+ server = provision(name, options)
155
+
156
+ wait_for_vms_to_appear(server, options)
157
+
158
+ update_guest_customization_options(server, options)
159
+ update_network_connection_options(server, options)
160
+ update_machine_resources(server, options)
161
+
162
+ power_on(server)
163
+
164
+ server
165
+ end
166
+
167
+ def launch_servers
168
+ super
169
+ end
170
+
171
+ def prepare_run
172
+ super
173
+ end
174
+ end
175
+ end
176
+ end