Project

General

Profile

« Previous | Next » 

Revision d1fa5fa3

Added by Romain Vrignaud over 10 years ago

fixes #1719 - Add support for GCE

View differences:

app/helpers/compute_resources_vms_helper.rb
actions << vm_power_action(vm)
end
actions << display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.id))
actions << display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity))
end
def default_available_actions(vm)
[vm_power_action(vm),
display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.id))]
display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity))]
end
def vpc_security_group_hash(security_groups)
app/models/compute_resource.rb
include Taxonomix
include Encryptable
encrypts :password
PROVIDERS = %w[ Libvirt Ovirt EC2 Vmware Openstack Rackspace].delete_if{|p| p == "Libvirt" && !SETTINGS[:libvirt]}
PROVIDERS = %w[ Libvirt Ovirt EC2 Vmware Openstack Rackspace GCE].delete_if{|p| p == "Libvirt" && !SETTINGS[:libvirt]}
audited :except => [:password, :attrs], :allow_mass_assignment => true
serialize :attrs, Hash
has_many :trends, :as => :trendable, :class_name => "ForemanTrend"
......
def provider_friendly_name
list = SETTINGS[:libvirt] ? ["Libvirt"] : []
list += %w[ oVirt EC2 VMWare OpenStack Rackspace ]
list += %w[ oVirt EC2 VMWare OpenStack Rackspace Google]
list[PROVIDERS.index(provider)] rescue ""
end
......
end
def create_vm args = {}
client.servers.create vm_instance_defaults.merge(args.to_hash.symbolize_keys)
options = vm_instance_defaults.merge(args.to_hash.symbolize_keys)
logger.debug("creating VM with the following options: #{options.inspect}")
client.servers.create options
rescue Fog::Errors::Error => e
logger.debug "Fog error: #{e.message}\n " + e.backtrace.join("\n ")
errors.add(:base, e.message.to_s)
app/models/compute_resources/foreman/model/gce.rb
module Foreman::Model
class GCE < ComputeResource
has_one :key_pair, :foreign_key => :compute_resource_id, :dependent => :destroy
before_create :setup_key_pair
validate :check_google_key_path
validates_presence_of :key_path, :project, :email
delegate :flavors, :to => :client
def to_label
"#{name} (#{zone}-#{provider_friendly_name})"
end
def capabilities
[:image]
end
def project
attrs[:project]
end
def project=(name)
attrs[:project] = name
end
def key_path
attrs[:key_path]
end
def key_path=(name)
attrs[:key_path] = name
end
def email
attrs[:email]
end
def email=(email)
attrs[:email] = email
end
#TODO: allow to select public / private ip address that foreman tries to find
def provided_attributes
super.merge({ :ip => :public_ip_address })
end
def vms
client.servers
end
def zones
client.list_zones.body['items'].map { |zone| zone['name'] }
end
def networks
client.list_networks.body['items'].map { |n| n['name'] }
end
def zone
url
end
def zone=(zone)
self.url = zone
end
def create_vm args = {}
#Dot are not allowed in names
args[:name] = args[:name].parameterize if args[:name].present?
args[:external_ip] = args[:external_ip] != '0'
args[:image_name] = args[:image_id]
username = images.where(:uuid => args[:image_name]).first.try(:username)
ssh = { :user => username, :public_key => key_pair.public }
super(args.merge(ssh))
rescue Exception => e
logger.debug "Unhandled GCE error: #{e.class}:#{e.message}\n " + e.backtrace.join("\n ")
errors.add(:base, e.message.to_s)
false
end
def available_images
client.images
end
def self.model_name
ComputeResource.model_name
end
def setup_key_pair
require 'sshkey'
name = "foreman-#{id}#{Foreman.uuid}"
key = ::SSHKey.generate
build_key_pair :name => name, :secret => key.private_key, :public => key.ssh_public_key
end
def test_connection(options = {})
super
errors[:user].empty? and errors[:password].empty? and zones
rescue => e
errors[:base] << e.message
end
private
def client
@client ||= ::Fog::Compute.new(:provider => 'google', :google_project => project, :google_client_email => email, :google_key_location => key_path)
end
def check_google_key_path
return if key_path.blank?
unless File.exists?(key_path)
errors.add(:key_path, _('Unable to access key'))
end
rescue => e
logger.warn("failed to access gce key path: #{e}")
logger.debug(e.backtrace)
errors.add(:key_path, e.message.to_s)
end
def vm_instance_defaults
super.merge(
:zone_name => zone,
:name => "foreman-#{Time.now.to_i}"
)
end
end
end
app/models/concerns/fog_extensions.rb
require 'fog/aws/models/compute/server'
Fog::Compute::AWS::Server.send(:include, FogExtensions::AWS::Server)
require 'fog/google'
require 'fog/google/models/compute/image'
Fog::Compute::Google::Image.send(:include, FogExtensions::Google::Image)
require 'fog/google/models/compute/server'
Fog::Compute::Google::Server.send(:include, FogExtensions::Google::Server)
require 'fog/google/models/compute/flavor'
Fog::Compute::Google::Flavor.send(:include, FogExtensions::Google::Flavor)
require 'fog/libvirt'
require 'fog/libvirt/models/compute/server'
Fog::Compute::Libvirt::Server.send(:include, FogExtensions::Libvirt::Server)
app/models/concerns/fog_extensions/google/flavor.rb
module FogExtensions
module Google
module Flavor
extend ActiveSupport::Concern
def id
name
end
end
end
end
app/models/concerns/fog_extensions/google/image.rb
module FogExtensions
module Google
module Image
extend ActiveSupport::Concern
def id
name
end
end
end
end
app/models/concerns/fog_extensions/google/server.rb
module FogExtensions
module Google
module Server
extend ActiveSupport::Concern
def pretty_machine_type
machine_type.split('/')[-1]
end
def flavors
service.flavors
end
end
end
end
app/views/compute_resources/form/_gce.html.erb
<%= text_f f, :project, :label => _("Google Project ID") %>
<%= text_f f, :email, :label => _("Client Email"), :class => 'span8' %>
<%= text_f f, :key_path, :label => _("Certificate path"), :help_inline => _('The file path where your p12 file is located'), :class => 'span8' %>
<% zones = f.object.zones rescue [] %>
<%= selectable_f(f, :zone, zones, {}, {:label => _('Zone'), :disabled => zones.empty?,
:help_inline => link_to_function(zones.empty? ? _("Load zones") : _("Test Connection"), "testConnection(this)",
:class => "btn + #{zones.empty? ? "" : "btn-success"}",
:'data-url' => test_connection_compute_resources_path) + image_tag('spinner.gif', :id => 'test_connection_indicator', :class => 'hide').html_safe }) %>
app/views/compute_resources_vms/form/_gce.html.erb
<%= select_f f, :machine_type, compute_resource.flavors, :id, :name %>
<%
arch ||= nil ; os ||= nil
images = possible_images(compute_resource, arch, os)
-%>
<div id='image_selection'>
<%= select_f f, :image_id, images, :uuid, :name,
{ :include_blank => (images.empty? || images.size == 1) ? false : _('Please select an image') },
{ :disabled => images.empty? } %>
</div>
<%= selectable_f f, :network, compute_resource.networks, {} %>
<%= checkbox_f f, :external_ip, :label => _('External IP') %>
app/views/compute_resources_vms/index/_gce.html.erb
<table class="table table-bordered" data-table="inline">
<thead>
<tr>
<th><%= _('Name') -%></th>
<th><%= _('Type') -%></th>
<th><%= _('State') -%></th>
<th></th>
</tr>
</thead>
<% @vms.each do |vm| -%>
<tr>
<td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity) %></td>
<td><%= vm.pretty_machine_type %></td>
<td><%= vm.state.downcase %></td>
<td>
<%= action_buttons(vm_power_action(vm), display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity))) %>
</td>
</tr>
<% end -%>
</table>
app/views/compute_resources_vms/show.html.erb
<% title_actions *available_actions(@vm),
show_console_action(@vm.ready?,
link_to_if_authorized(_("Console"), hash_for_console_compute_resource_vm_path, {:disabled => @vm.nil? || !@vm.ready?, :class => "btn btn-info"})),
display_link_if_authorized(_("Associate VM"), hash_for_associate_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => @vm.id), :title=> _("Associate VM to a Foreman host"), :method => :put, :class=>"btn"),
display_link_if_authorized(_("Associate VM"), hash_for_associate_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => @vm.identity), :title=> _("Associate VM to a Foreman host"), :method => :put, :class=>"btn"),
link_to(_("Back"), compute_resource_path(@compute_resource), :class=>'btn')
%>
app/views/compute_resources_vms/show/_gce.html.erb
<% title @vm.name %>
<div class='span12'>
<table class="table table-bordered table-striped">
<tr>
<th colspan="2">Properties</th>
</tr>
<tr>
<td>State</td>
<td><%= @vm.state.downcase %></td>
</tr>
<tr>
<td>Machine Type</td>
<td><%= @vm.pretty_machine_type %></td>
</tr>
<tr>
<td>Zone</td>
<td><%= @vm.zone %></td>
</tr>
<tr>
<td>Private IP</td>
<td><%= @vm.private_ip_address %></td>
</tr>
<% if @vm.public_ip_address.present? -%>
<tr>
<td>Public IP</td>
<td><%= @vm.public_ip_address %></td>
</tr>
<% end -%>
</table>
</div>
bundler.d/gce.rb
group :gce do
gem "google-api-client"
gem 'sshkey'
end
db/migrate/20130804131949_add_public_to_key_pairs.rb
class AddPublicToKeyPairs < ActiveRecord::Migration
def change
add_column :key_pairs, :public, :text
end
end

Also available in: Unified diff