Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions lib/puppet/provider/cloudstack_instance/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
def self.instances
# I may need to fail if the server does not have a name?
connection.servers.collect do |server|
if server.state != 'Destroyed'
if (server.state != 'Destroyed') and (server.state != 'Stopping')
Puppet.debug("Found #{server.display_name} in state: #{server.state}")
Puppet.debug("Found #{server.display_name} in state: #{server.state}")
if server.nics.size > 1
raise(Puppet::Error, "Does not support dual nics (it is just a prototype")
end
Expand All @@ -26,7 +28,8 @@ def self.instances
:host => server.host_name,
:state => server.state.downcase,
:group => server.group,
#:keypair => server.keypair,
# for some reason the keypair does not seem to be returned from listvminstnaces
:keypair => server.key_pair,
:ensure => :present
# I may want to print network information here
)
Expand All @@ -47,22 +50,26 @@ def create
:image_id => #{image_id},
:flavor_id => #{flavor_id},
:zone_id => #{zone_id},
:network_ids => #{network_id}
:network_ids => #{network_id},
:keypair => #{resource[:keypair]},
")
connection.servers.bootstrap(
response = connection.servers.bootstrap(
:display_name => resource[:name],
:image_id => image_id,
:flavor_id => flavor_id,
:zone_id => zone_id,
:network_ids => network_id,
:key_pair => resource[:keypair],
:group => resource[:group]
#:keypair => resource[:keypair]
)
@property_hash[:ensure] = :present
@property_hash[:internal_ipaddress] = response.nics.first['ipaddress']
end

def destroy
# TODO need to block until delete is completed
connection.servers.destroy(@property_hash[:id])
connection.servers.destroy(@property_hash[:id])
@property_hash[:ensure] = :absent
end

def internal_ipaddress
Expand Down
24 changes: 17 additions & 7 deletions lib/puppet/provider/cloudstack_keypair/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

def self.instances
# I may need to fail if the server does not have a name?
connection.list_ssh_key_pairs['listsshkeypairsresponse']['sshkeypair'].collect do |keypair|
#require 'ruby-debug';debugger
(connection.list_ssh_key_pairs['listsshkeypairsresponse']['sshkeypair'] || []).collect do |keypair|
new(
:name => keypair['name'],
:fingerprint => keypair['fingerprint'],
Expand All @@ -18,6 +17,10 @@ def self.instances
end
end

def exists?
@property_hash[:ensure] == :present
end

def create
#domain_id = get_domain_id(resource[:domain])
#project_id = get_project_id(resource[:project])
Expand All @@ -41,6 +44,7 @@ def create
end
Puppet.info("Writing your private key to #{key_file}")
File.new(key_file, 'w').write(response['privatekey'])
File.chmod(0600, key_file)
end
@property_hash[:fingerprint] = response['fingerprint']
@property_hash[:privatekey] = response['privatekey']
Expand All @@ -53,7 +57,13 @@ def destroy

def privatekey
if fingerprint
File.read(key_file_path(fingerprint))
key_file = key_file_path(fingerprint)
if File.exists?(key_file)
File.read(key_file)
else
Puppet.notice("Could not find locally cached private key for #{resource[:name]}")
nil
end
else
fail('Expected fingerprint to be set')
end
Expand All @@ -67,8 +77,8 @@ def key_file_path(fingerprint_arg=fingerprint)
File.join(Puppet[:confdir], 'cloudstack', 'keypair', fingerprint_arg, 'id_rsa')
end

def flush
@property_hash = resource.to_hash
end
# def flush
# @property_hash = resource.to_hash
# end

end
end
2 changes: 1 addition & 1 deletion lib/puppet/provider/cloudstack_network/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
mk_resource_methods

def self.instances
connection.list_networks['listnetworksresponse']['network'].collect do |net|
(connection.list_networks['listnetworksresponse']['network'] || []).collect do |net|
new(
:name => net['name'],
:id => net['id'],
Expand Down
106 changes: 99 additions & 7 deletions lib/puppet/provider/puppet_node/ssh.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,95 @@
# end

def role
# make an ssh call? decide if the role is already installed?
false
# ssh into the instance to see if puppet is already installed
# TODO eventually, this should also check the version
begin
results = run_remote_puppet_script('check_puppet_version')
rescue Puppet::Error => e
return nil
end
version = results['stdout'].split("\n").last.strip
Puppet.info("Puppet version #{version} is currently installed")
version
end

def role=(role)
machinehostname(resource[:machine])
getprivatekey(resource[:keypair])
if role == :master
if role == :pe_master
script_name = 'pe_master'
elsif role == :pe_agent
script_name = 'pe_agent'
else
fail("unsupported role #{role.to_s}")
end
run_remote_puppet_script(script_name)
end

def run_remote_puppet_script(script_name)

hostname = machinehostname(resource[:machine])
keyfile = getprivatekey(resource[:keypair])

if script_name == 'pe_master'
answers = 'https://raw.github.com/gist/4229037/d03453ae789797909745b63d44611adc5090c050/gistfile1.txt'
modulepath = '/etc/puppetlabs/puppet/modules'
elsif script_name == 'pe_agent'
answers = 'https://raw.github.com/gist/3299812/9dea06dba93d218c61e5fa9d9e928a265c137239/answers'
else
modulepath = '/etc/puppet/modules'
Puppet.debug("Script name: #{script_name} does not need an answers file")
end

Puppet.info("Running script #{script_name} via ssh to '#{hostname}' with key: '#{keyfile}'")

write_name = "#{hostname}-#{Time.now.to_i}"
unless File.exists?(script_write_dir)
Puppet.info('Creating script dir')
Dir.mkdir(script_write_dir)
end

compile_options = {
'installer_payload' => resource[:installer_payload],
'answers_payload' => answers,
'certname' => machinehostname(resource[:machine]),
'puppetmaster' => machinehostname(resource[:puppetmaster]),
'facts' => {'classes' => resource['classes'].to_pson},
'modulepath' => modulepath
}

Puppet.debug("Compiling script #{script_name} with options:\n#{compile_options.inspect}")

File.open(File.join(script_write_dir, "#{write_name}.erb"), 'w') do |fh|
fh.write(
compile_erb(File.join(script_dir, "#{script_name}.erb"), compile_options)
)
end

require 'puppet/cloudpack'

Puppet.info("running: ssh root@#{hostname} -i '#{keyfile}'")
Puppet::CloudPack.install(hostname,
{
:install_script => write_name,
:keyfile => keyfile,
:login => 'root'

}
)
end

def modules

end

def getprivatekey(key)
fail('Must specify a private key') unless key
key = key.to_s
if key =~ /(Cloudstack_keypair\[(\S+)?\])/
return model.catalog.resource($1).provider.key_file_path
else
puts 'It is a path'
return key
end
key
end

Expand All @@ -41,15 +113,35 @@ def machinehostname(id)
raise(Puppet::Error, "Do not support things other than internal_ip at the moment")
else
# assume we want the internal ip address
res.provider.internal_ipaddress
if res.provider.exists?
res.provider.internal_ipaddress
else
fail("Referenced cloudstack instance #{$1} does not exist")
end
end
else
raise(Puppet::Error, "Puppet resource #{$1} was set at the machine, but not found in the catalog, try hostname instead")
fail("Puppet resource #{$1} was set at the machine, but not found in the catalog, try hostname instead")
end
else
# otherwise assume id is the hostname of ip address
return id
end
end

def compile_erb(filename, options)
ERB.new(File.read(filename)).result(binding)
end

def script_write_dir
File.join(Puppet[:confdir], 'scripts')
end

def script_dir
File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'templates'))
end

def find_template(name)
File.expand_path(File.join(script_dir, "#{name}.erb"))
end

end
24 changes: 20 additions & 4 deletions lib/puppet/type/cloudstack_instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@
desc 'internal ip address. Puppet assumes that instances can only have one'
end

newproperty(:keypair) do
desc 'keypair to associate with system'
end

newproperty(:flavor) do
desc 'name of flavor'
end
Expand Down Expand Up @@ -78,7 +74,27 @@ def insync?(is)
end
end

newparam(:userdata) do
desc 'used to set userdata when an instance is created'
end

newproperty(:keypair) do
desc 'keypair to associate with system'
munge do |value|
value = value.to_s
if value =~ /^Cloudstack_keypair\[(\S+)\]/
$1
elsif value =~ /^(\S+)\[(\S+)\]/
fail("#{$1} is not a valid type, expected Cloudstack_keypair")
else
value
end
end
end

autorequire(:cloudstack_keypair) do
[self[:keypair]]
end
# this causes querying the resources to fail ;(
#validate do
# unless self[:zone_id] and self[:template_id] and self[:service_offering_id]
Expand Down
55 changes: 47 additions & 8 deletions lib/puppet/type/puppet_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,66 @@
desc 'classes to apply to the node. Only makes sense for an agent'
end

newparam(:machine) do
desc 'machine where puppet actions are performed. Takes a reference or a hostname/ipaddress'
end

newproperty(:modules) do
desc 'modules to install from the forge (only makes sense for a master role'
end

newproperty(:role) do
desc 'role of this puppet instance'
newvalues(:agent, :master, :pe_agent, :pe_master, :apply)
def insync?(is)
return is =~ /^\d+\.\d+\.\d+/
end
end

newparam(:installer_payload) do
# right now this is hardcoded to rhel 5 pe installation payload
defaultto 'https://s3.amazonaws.com/pe-builds/released/2.6.1/puppet-enterprise-2.6.1-el-5-x86_64.tar.gz'
end

newparam(:answers_payload) do
defaultto 'https://raw.github.com/gist/4229037/d03453ae789797909745b63d44611adc5090c050/gistfile1.txt'
end

newparam(:puppetmaster) do
desc 'hostname of puppetmaster to connect to. Used by pe_agent and agent roles.'
end

newparam(:facts) do
desc 'hash of facts that can be used to set instance role'
defaultto {}
end

newparam(:keypair) do
desc 'keypair to use to connect'
validate do |value|
unless value.to_s =~ /^Cloudstack_keypair\[(\S+)\]/
fail("#{value} is not a valid keypair reference, expected Cloudstack_keypair[<name>]")
end
end
end

newproperty(:role) do
desc 'role of this puppet instance'
newvalues(:agent, :master)
newparam(:machine) do
desc 'machine where puppet actions are performed. Takes a reference or a hostname/ipaddress'
end

autorequire(:cloudstack_keypair) do
if self[:keypair].to_s =~ /^Cloudstack_keypair\[(\S+)\]/
[$1]
else
[]
end
end

autorequire(:cloudstack_instance) do
instances = []
if self[:machine].to_s =~ /Cloudstack_instance\[(\S+?)\](\[\S+?\])?/
[$1]
instances.push($1)
end
if self[:puppetmaster].to_s =~ /Cloudstack_instance\[(\S+?)\](\[\S+?\])?/
instances.push($1)
end
instances
end

end
11 changes: 11 additions & 0 deletions templates/agent.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
#
# script that performs classification using puppet agent
#

set -u
set -e

<%= ERB.new(File.read(find_template('fragments/options')), nil, "<>", '_options').result(binding) %>

puppet agent --pluginsync --test --certname=<%= options['certname'] %> --server=<%= options['puppetmaster'] %> | tee /tmp/puppet_output.log
13 changes: 13 additions & 0 deletions templates/apply.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
#
# script that performs classification using puppet apply
#

set -u
set -e

<% if options['install_puppet'] %>
<%= ERB.new(File.read(find_template('fragments/install_poss')), nil, "<>", '_install_poss').result(binding) %>
<% end %>

<%= ERB.new(File.read(find_template('fragments/install_poss')), nil, "<>", '_install_poss').result(binding) %>
Loading