Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vsphere support #504

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be a good idea to create a group for vsphere (similar to the one for kvm) which holds the rbvmomi gem.

group :test do
gem "rake"
gem "em-winrm", :git => 'git://github.com/hh/em-winrm.git', :ref => '31745601d3'
gem "em-winrm", :git => 'http://github.com/hh/em-winrm.git', :ref => '31745601d3'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes to em-winrm probably should not go into this pull request

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't disagree, though I do have to maintain this as a local change because git:// does not work within my corporate network. The change above wasn't actually meant to be included in the commits, just haven't had a change to get it changed back.

#gem "chef"
#gem "knife-windows"
end
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Depending on how you want to use veewee, read through one of the following guide
- [guide for Virtualbox](https://github.com/jedi4ever/veewee/tree/master/doc/vbox.md)
- [guide for Vmware fusion](https://github.com/jedi4ever/veewee/tree/master/doc/fusion.md)
- [guide for KVM](https://github.com/jedi4ever/veewee/tree/master/doc/kvm.md)
- [guide for VMware vSphere](https://github.com/jedi4ever/veewee/tree/master/doc/vsphere.md)

You can also look at the more detailed pages on each subject in the [documentation directory](https://github.com/jedi4ever/veewee/tree/master/doc)

Expand Down
84 changes: 84 additions & 0 deletions doc/vsphere.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
The veewee vSphere provider adds the capability to create base boxes/VMs on VMware ESXi/vSphere and vCenter servers. Because this capability leverages a centralized hypervisor (rather than a local hypervisor on the machien running the veewee command), there are some considerations when building a machine that are unique to this provider.

## Credentials

vSphere/vCenter require a users to authenticate actions the remote server in order to perform management actions. User credentials can be specified in 3 ways, in order of precedence:

1. Use the -h (i.e., host), -u (i.e., user), and -p (i.e., password) options with each `veewee vsphere` command
2. Set the `VEEWEE_VSPHERE_AUTHFILE` environment variable to the path of a YAML files with the server hostname and credentials
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better if the .fog YAML file would be used to configure vsphere? It's not well documented, but should go into the users home folder e.g. to ~/.fog and may contain the config parameters :vsphere_username, :vsphere_password, :vsphere_server :vsphere_port, :vsphere_path, :vsphere_ns :vsphere_rev, :vsphere_ssl and :vsphere_expected_pubkey_hash

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of my original thoughts was to have the stored credentials in a predefined location within the users home directory, however it wasn't clear to me how this would work with use cases where veewee would be called by an automated process (e.g., Jenkins running a CI job) that may not be provided a traditional home directory (i.e., /home/user) and where its home location may be part of a path may be used by a service that has no business in knowing the values directly. The environment approach also provides the ability to have several definitions defined within a single automation server, and just varying the environmental configuration per job. For example, this could support building different environments or baselines during development.

I think it would be reasonable to check for ~/.fog to use in configuring the service, effectively looking for that file if the environment variable is not set. Using ~/.fog has the downside of having a configuration file that isn't explicitly associated with veewee, but this is perhaps a minor nit. I'm not completely convinced one way or another at this point. Near term, I will look at updating the current YAML structure to make the .fog file's parameter names to assist those that use both.

3. The host and user can be provided by either method above, and veewee vsphere provider will prompt the user for the password.

Note, a `VEEWEE_VSPHERE_AUTHFILE` YAML file contains credentials to login to a given vSphere/vCenter server. It is the responsibility of the user to ensure this file has appropriate access controls applied to ensure proper security compliance.

### YAML configuration format

```yaml
---
host: vcenter.example.com
user: vsphere-user
password: vsphere-password
```

## Networking support

vSphere does not have built in support concepts like a NAT network configuration within the hypervisor itself. This means that when creating a virtual machine, a veewee vSphere user needs to have the appropriate networking capability in place prior to issuing vSphere build commands. In addition, there are special steps that must be completed within definition.rb and kickstart or preseed.cfg files to ensure network support works as expected.

### Virtual Network

Each vSphere/vCenter enviornment has a series of virtual networks that can be added to an individual virtual machine. Veewee must specify the virtual network when building a vSphere VM to make sure the remaining commands can be executed as expected. The virtual network can be set in 3 ways, listed below in the order of precedence:

1. `--net` option provided with the `veewee vsphere build` command
2. Contents of the definition.rb vsphere properties `:vsphere => { :vm_options => { :network => "VM Network" } }`
3. Default Virtual Network

Along with the above virtual network definition, the following assumptions are made about the state of the network:

* The network includes a DHCP server that can provide a new VM with an IP address to communicate on the network
* The host issuing the veewee command has a route to the VM after its created on the virtual network

### Veewee Host IP

Because we cannot assume anything about the network topology between the veewee host and the virtual network attached to a VMware VM build using the vSphere provider, the IP address used to serve the kickstart or preseed.cfg file must be specified as part of the definition.rb file. This can be configured using the following:

:vsphere => { :kickstart_ip => "192.168.0.1" }

veewee will raise an error if this configuration is not complete.

## Data Location

vSphere defines storage for VMs and files as a series of datastores within a given server or cluster. The vSphere provider must specify a datastore to build a given VM in. The datastore can be set in 3 ways, listed below in the order of precedence:

1. `--ds` option provided with the `veewee vsphere build` command
2. Contents of the definition.rb vsphere properties `:vsphere => { :vm_options => { :datastore => "datastore" } }`
3. Default datastore (i.e., first datastore in the servers list)

Note, the datastore location chosen is used for both creating the VM and its associated files, and also for uploading ISO files used to build the VM.

## VMware Tools
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a good idea to put the VMware tools setup in a vsphere.sh postinstall file which will be automatically included in the list of postinstall files only if the vsphere provider is used. This would require some changes on handling the postinstall files, but I would very much vote for such a change. See also issue #505 for more details.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per my notes, the VMware tools need to be installed during the OS installation process to allow the guest IP to be retrieve so postinstall processing to work. If there are straightforward ways to retrieve the IP address without the tools installed (and from some other links I've seen today there might be) the approach you suggest could work. But, barring an alternate method to get an IP address it needs to be part of the preseed or kickstart process.


In order to complete postinstall.sh scripts, veewee has to be able to determine the created VMs IP address after OS installation is complete. vSphere/vCenter do not have a method to retrieve this information withou the VMware Tools--the VMware equivalent of VirtualBox Guest Additions--installed on the guest OS. Therefore, the kickstart or preseed.cfg files used to automated the OS installation must be modified in order to install VMware Tools prior to the OS installation completing. This will allow post installation actions to execute as expected.

### RHEL/CentOS Instructions

Follow the instructions from VMware for installing VMware tools on a RHEL platform as seen at the following link: http://www.vmware.com/download/packages.html.

### Ubuntu Instructions

On Ubuntu distributions, VMware tools can be added by installing the open-vm-tools from the multiverse repository inside the preseed.cfg file. For example:

d-i pkgsel/include string openssh-server open-vm-tools

There can be only one instance of this within a preseed.cfg file, so ensure that if you add open-vm-tools to the existing command as provided.

## External tool pre-requisites

The following tools are needed to support the execution of the vSphere provider:

* curl - used to upload files to a vSphere datastore (specifically to support ISO uploads). This will hopefully be corrected in later revisions to replace curl with a native ruby solution
* ovftool - used for the `veewee vsphere export` command to export a VM from the vSphere/vCenter server to an ova file

The veewee vSphere provider assumes both of these tools are in the user's path.

## Command Timing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually is quite a big issue. Several templates (e.g. Ubuntu 12.04) seem to have very short boot delays. If possible the boot delays should be increased to safe values for all templates. Again something we might want to handle in issue #505 and follow ups to it.


Working with a remote server increases the likelihood that veewee processes that depend on waits or delays to execute a given command will fail leaving veewee and the guest VM being built in an unstable state. Users should consider increasing timeouts if issues appear where steps are out of sequence.
1 change: 1 addition & 0 deletions lib/veewee/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ module Command
require 'veewee/command/vbox'
require 'veewee/command/fusion'
require 'veewee/command/parallels'
require 'veewee/command/vsphere'
172 changes: 172 additions & 0 deletions lib/veewee/command/vsphere.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
module Veewee
module Command
class Vsphere< Veewee::Command::GroupBase

register "vsphere", "Subcommand for Vmware vsphere"
desc "build [BOX_NAME]", "Build box"
method_option :vsphere_host,:type => :string , :default => nil, :aliases => "-h", :desc => "VSphere Host"
method_option :vsphere_user,:type => :string , :default => nil, :aliases => "-u", :desc => "VSphere User"
method_option :vsphere_password,:type => :string , :default => nil, :aliases => "-p", :desc => "VSphere Password"
method_option :datastore,:type => :string , :default => nil, :aliases => "--ds", :desc => "VSphere Datastore"
method_option :network,:type => :string , :default => nil, :aliases => "--net", :desc => "VSphere Virtual Network"
method_option :force,:type => :boolean , :default => false, :aliases => "-f", :desc => "force the build"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
method_option :nogui,:type => :boolean , :default => false, :aliases => "-n", :desc => "no gui"
method_option :auto,:type => :boolean , :default => false, :aliases => "-a", :desc => "auto answers"
method_option :postinstall_include, :type => :array, :default => [], :aliases => "-i", :desc => "ruby regexp of postinstall filenames to additionally include"
method_option :postinstall_exclude, :type => :array, :default => [], :aliases => "-e", :desc => "ruby regexp of postinstall filenames to exclude"
def build(box_name)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.providers["vsphere"].get_box(box_name).build(options)
end

method_option :vsphere_host,:type => :string , :default => nil, :aliases => "-h", :desc => "VSphere Host"
method_option :vsphere_user,:type => :string , :default => nil, :aliases => "-u", :desc => "VSphere User"
method_option :vsphere_password,:type => :string , :default => nil, :aliases => "-p", :desc => "VSphere Password"
method_option :force,:type => :boolean , :default => false, :aliases => "-f", :desc => "force the destroy"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
method_option :nogui,:type => :boolean , :default => false, :aliases => "-n", :desc => "no gui"
desc "destroy [BOXNAME]", "Destroys the virtualmachine that was built"
def destroy(box_name)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.providers["vsphere"].get_box(box_name).destroy(options)
end

method_option :vsphere_host,:type => :string , :default => nil, :aliases => "-h", :desc => "VSphere Host"
method_option :vsphere_user,:type => :string , :default => nil, :aliases => "-u", :desc => "VSphere User"
method_option :vsphere_password,:type => :string , :default => nil, :aliases => "-p", :desc => "VSphere Password"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
method_option :force,:type => :boolean , :default => false, :aliases => "-f", :desc => "force the shutdown"
desc "halt [BOXNAME]", "Activates a shutdown the virtualmachine"
def halt(box_name)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.providers["vsphere"].get_box(box_name).halt(options)
end

method_option :vsphere_host,:type => :string , :default => nil, :aliases => "-h", :desc => "VSphere Host"
method_option :vsphere_user,:type => :string , :default => nil, :aliases => "-u", :desc => "VSphere User"
method_option :vsphere_password,:type => :string , :default => nil, :aliases => "-p", :desc => "VSphere Password"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
method_option :nogui,:type => :boolean , :default => false, :aliases => "-n", :desc => "no gui"
desc "up [BOXNAME]", "Starts a Box"
def up(box_name)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.providers["vsphere"].get_box(box_name).up(options)
end

desc "scp [BOXNAME] [LOCAL FILE] [REMOTE FILE]", "SCP to box"
method_option :vsphere_host,:type => :string , :default => nil, :aliases => "-h", :desc => "VSphere Host"
method_option :vsphere_user,:type => :string , :default => nil, :aliases => "-u", :desc => "VSphere User"
method_option :vsphere_password,:type => :string , :default => nil, :aliases => "-p", :desc => "VSphere Password"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
def scp(box_name,local_file,remote_file)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.providers["vsphere"].get_box(box_name).scp(local_file,remote_file)
end

desc "ssh [BOXNAME] [COMMAND]", "SSH to box"
method_option :vsphere_host,:type => :string , :default => nil, :aliases => "-h", :desc => "VSphere Host"
method_option :vsphere_user,:type => :string , :default => nil, :aliases => "-u", :desc => "VSphere User"
method_option :vsphere_password,:type => :string , :default => nil, :aliases => "-p", :desc => "VSphere Password"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
def ssh(box_name,command=nil)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.providers["vsphere"].get_box(box_name).issh(command)
end

desc "define [BOXNAME] [TEMPLATE]", "Define a new basebox starting from a template"
method_option :force,:type => :boolean , :default => false, :aliases => "-f", :desc => "overwrite the definition"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
def define(definition_name, template_name)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.definitions.define(definition_name,template_name,options)
env.ui.info "The basebox '#{definition_name}' has been succesfully created from the template '#{template_name}'"
env.ui.info "You can now edit the definition files stored in definitions/#{definition_name} or build the box with:"
env.ui.info "veewee vsphere build '#{definition_name}'"
end

desc "undefine [BOXNAME]", "Removes the definition of a basebox "
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
def undefine(definition_name)
env.ui.info "Removing definition #{definition_name}" , :prefix => false
begin
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.definitions.undefine(definition_name,options)
env.ui.info "Definition #{definition_name} succesfully removed",:prefix => false
rescue Error => ex
env.ui.error "#{ex}" , :prefix => false
exit -1
end
end

desc "validate [NAME]", "Validates a box against vsphere compliancy rules"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
method_option :tags, :type => :array , :default => %w{vsphere puppet chef}, :aliases => "-t", :desc => "tags to validate"
def validate(box_name)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.providers["vsphere"].get_box(box_name).validate_vsphere(options)
end

desc "ostypes", "List the available Operating System types"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
def ostypes
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.ostypes.each do |name|
env.ui.info "- #{name}"
end
end

desc "path [NAME]", "Prints the vms path"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
method_option :force,:type => :boolean , :default => false, :aliases => "-f", :desc => "overwrite existing file"
def path(box_name)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
env.ui.info venv.providers["vsphere"].get_box(box_name).path
end

desc "export [NAME]", "Exports the basebox to the ova format"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
method_option :force,:type => :boolean , :default => false, :aliases => "-f", :desc => "overwrite existing file"
def export(box_name)
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.providers["vsphere"].get_box(box_name).export_ova(options)
end

desc "templates", "List the currently available templates"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
def templates
env.ui.info "The following templates are available:",:prefix => false
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.templates.each do |name,template|
env.ui.info "veewee vsphere define '<box_name>' '#{name}'",:prefix => false
end
end

desc "list", "Lists all defined boxes"
method_option :debug,:type => :boolean , :default => false, :aliases => "-d", :desc => "enable debugging"
def list
env.ui.info "The following local definitions are available:",:prefix => false
venv=Veewee::Environment.new(options)
venv.ui=env.ui
venv.definitions.each do |name,definition|
env.ui.info "- #{name}"
end
end

end

end
end
Loading