summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
authorPieter van de Bruggen <pieter@puppetlabs.com>2011-04-18 13:40:31 -0700
committerPieter van de Bruggen <pieter@puppetlabs.com>2011-04-18 14:08:32 -0700
commit07b677c5f6af8def03c5c30393fd83bc3986239a (patch)
treeb28d3376b4ea835a2348ebfea898d674cb593e34 /lib/puppet
parentb142973a94ced6c0ff43da882189abe806c18c68 (diff)
downloadpuppet-07b677c5f6af8def03c5c30393fd83bc3986239a.tar.gz
puppet-07b677c5f6af8def03c5c30393fd83bc3986239a.tar.xz
puppet-07b677c5f6af8def03c5c30393fd83bc3986239a.zip
Merge remote-tracking branch 'community/feature/puppet-device' into 2.7.x
Reviewed-By: Mike Stahnke
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/application/device.rb255
-rw-r--r--lib/puppet/defaults.rb5
-rw-r--r--lib/puppet/indirector/facts/network_device.rb25
-rw-r--r--lib/puppet/provider/cisco.rb9
-rw-r--r--lib/puppet/provider/interface/cisco.rb18
-rw-r--r--lib/puppet/provider/network_device.rb21
-rw-r--r--lib/puppet/provider/vlan/cisco.rb14
-rw-r--r--lib/puppet/resource/catalog.rb1
-rw-r--r--lib/puppet/transaction.rb4
-rw-r--r--lib/puppet/type.rb28
-rw-r--r--lib/puppet/type/interface.rb2
-rwxr-xr-xlib/puppet/type/schedule.rb2
-rw-r--r--lib/puppet/type/vlan.rb2
-rw-r--r--lib/puppet/util/command_line.rb3
-rw-r--r--lib/puppet/util/network_device.rb12
-rw-r--r--lib/puppet/util/network_device/base.rb32
-rw-r--r--lib/puppet/util/network_device/cisco/device.rb10
-rw-r--r--lib/puppet/util/network_device/cisco/facts.rb72
-rw-r--r--lib/puppet/util/network_device/config.rb93
-rw-r--r--lib/puppet/util/network_device/transport.rb4
-rw-r--r--lib/puppet/util/network_device/transport/ssh.rb4
21 files changed, 565 insertions, 51 deletions
diff --git a/lib/puppet/application/device.rb b/lib/puppet/application/device.rb
new file mode 100644
index 000000000..df5bac26a
--- /dev/null
+++ b/lib/puppet/application/device.rb
@@ -0,0 +1,255 @@
+require 'puppet/application'
+require 'puppet/util/network_device'
+
+
+class Puppet::Application::Device < Puppet::Application
+
+ should_parse_config
+ run_mode :agent
+
+ attr_accessor :args, :agent, :host
+
+ def preinit
+ # Do an initial trap, so that cancels don't get a stack trace.
+ trap(:INT) do
+ $stderr.puts "Cancelling startup"
+ exit(0)
+ end
+
+ {
+ :waitforcert => nil,
+ :detailed_exitcodes => false,
+ :verbose => false,
+ :debug => false,
+ :centrallogs => false,
+ :setdest => false,
+ }.each do |opt,val|
+ options[opt] = val
+ end
+
+ @args = {}
+ end
+
+ option("--centrallogging")
+ option("--debug","-d")
+ option("--verbose","-v")
+
+ option("--detailed-exitcodes") do |arg|
+ options[:detailed_exitcodes] = true
+ end
+
+ option("--logdest DEST", "-l DEST") do |arg|
+ begin
+ Puppet::Util::Log.newdestination(arg)
+ options[:setdest] = true
+ rescue => detail
+ puts detail.backtrace if Puppet[:debug]
+ $stderr.puts detail.to_s
+ end
+ end
+
+ option("--waitforcert WAITFORCERT", "-w") do |arg|
+ options[:waitforcert] = arg.to_i
+ end
+
+ option("--port PORT","-p") do |arg|
+ @args[:Port] = arg
+ end
+
+ def help
+ <<-HELP
+
+puppet-device(8) -- Manage remote network devices
+========
+
+SYNOPSIS
+--------
+Retrieves all configurations from the puppet master and apply
+them to the remote devices configured in /etc/puppet/device.conf.
+
+Currently must be run out periodically, using cron or something similar.
+
+USAGE
+-----
+ puppet device [-d|--debug] [--detailed-exitcodes] [-V|--version]
+ [-h|--help] [-l|--logdest syslog|<file>|console]
+ [-v|--verbose] [-w|--waitforcert <seconds>]
+
+
+DESCRIPTION
+-----------
+Once the client has a signed certificate for a given remote device, it will
+retrieve its configuration and apply it.
+
+USAGE NOTES
+-----------
+One need a /etc/puppet/device.conf file with the following content:
+
+[remote.device.fqdn]
+type <type>
+url <url>
+
+where:
+ * type: the current device type (the only value at this time is cisco)
+ * url: an url allowing to connect to the device
+
+Supported url must conforms to:
+ scheme://user:password@hostname/?query
+
+ with:
+ * scheme: either ssh or telnet
+ * user: username, can be omitted depending on the switch/router configuration
+ * password: the connection password
+ * query: this is device specific. Cisco devices supports an enable parameter whose
+ value would be the enable password.
+
+OPTIONS
+-------
+Note that any configuration parameter that's valid in the configuration file
+is also a valid long argument. For example, 'server' is a valid configuration
+parameter, so you can specify '--server <servername>' as an argument.
+
+* --debug:
+ Enable full debugging.
+
+* --detailed-exitcodes:
+ Provide transaction information via exit codes. If this is enabled, an
+ exit code of '2' means there were changes, and an exit code of '4' means
+ that there were failures during the transaction. This option only makes
+ sense in conjunction with --onetime.
+
+* --help:
+ Print this help message
+
+* --logdest:
+ Where to send messages. Choose between syslog, the console, and a log file.
+ Defaults to sending messages to syslog, or the console if debugging or
+ verbosity is enabled.
+
+* --verbose:
+ Turn on verbose reporting.
+
+* --waitforcert:
+ This option only matters for daemons that do not yet have certificates
+ and it is enabled by default, with a value of 120 (seconds). This causes
+ +puppet agent+ to connect to the server every 2 minutes and ask it to sign a
+ certificate request. This is useful for the initial setup of a puppet
+ client. You can turn off waiting for certificates by specifying a time
+ of 0.
+
+EXAMPLE
+-------
+ $ puppet device --server puppet.domain.com
+
+AUTHOR
+------
+Brice Figureau
+
+
+COPYRIGHT
+---------
+Copyright (c) 2011 Puppet Labs, LLC
+Licensed under the Apache 2.0 License
+ HELP
+ end
+
+
+ def main
+ vardir = Puppet[:vardir]
+ confdir = Puppet[:confdir]
+ certname = Puppet[:certname]
+
+ # find device list
+ require 'puppet/util/network_device/config'
+ devices = Puppet::Util::NetworkDevice::Config.devices
+ if devices.empty?
+ Puppet.err "No device found in #{Puppet[:deviceconfig]}"
+ exit(1)
+ end
+ devices.each_value do |device|
+ begin
+ Puppet.info "starting applying configuration to #{device.name} at #{device.url}"
+
+ # override local $vardir and $certname
+ Puppet.settings.set_value(:confdir, File.join(Puppet[:devicedir], device.name), :cli)
+ Puppet.settings.set_value(:vardir, File.join(Puppet[:devicedir], device.name), :cli)
+ Puppet.settings.set_value(:certname, device.name, :cli)
+
+ # this will reload and recompute default settings and create the devices sub vardir, or we hope so :-)
+ Puppet.settings.use :main, :agent, :ssl
+
+ # this init the device singleton, so that the facts terminus
+ # and the various network_device provider can use it
+ Puppet::Util::NetworkDevice.init(device)
+
+ # ask for a ssl cert if needed, but at least
+ # setup the ssl system for this device.
+ setup_host
+
+ require 'puppet/configurer'
+ configurer = Puppet::Configurer.new
+ report = configurer.run(:network_device => true)
+ rescue => detail
+ puts detail.backtrace if Puppet[:trace]
+ Puppet.err detail.to_s
+ ensure
+ Puppet.settings.set_value(:vardir, vardir, :cli)
+ Puppet.settings.set_value(:confdir, confdir, :cli)
+ Puppet.settings.set_value(:certname, certname, :cli)
+ end
+ end
+ end
+
+ # Handle the logging settings.
+ def setup_logs
+ if options[:debug] or options[:verbose]
+ Puppet::Util::Log.newdestination(:console)
+ if options[:debug]
+ Puppet::Util::Log.level = :debug
+ else
+ Puppet::Util::Log.level = :info
+ end
+ end
+
+ Puppet::Util::Log.newdestination(:syslog) unless options[:setdest]
+ end
+
+ def setup_host
+ @host = Puppet::SSL::Host.new
+ waitforcert = options[:waitforcert] || (Puppet[:onetime] ? 0 : 120)
+ cert = @host.wait_for_cert(waitforcert)
+ end
+
+ def setup
+ setup_logs
+
+ args[:Server] = Puppet[:server]
+ if options[:centrallogs]
+ logdest = args[:Server]
+
+ logdest += ":" + args[:Port] if args.include?(:Port)
+ Puppet::Util::Log.newdestination(logdest)
+ end
+
+ Puppet.settings.use :main, :agent, :device, :ssl
+
+ # Always ignoreimport for agent. It really shouldn't even try to import,
+ # but this is just a temporary band-aid.
+ Puppet[:ignoreimport] = true
+
+ # We need to specify a ca location for all of the SSL-related i
+ # indirected classes to work; in fingerprint mode we just need
+ # access to the local files and we don't need a ca.
+ Puppet::SSL::Host.ca_location = :remote
+
+ Puppet::Transaction::Report.indirection.terminus_class = :rest
+
+ # Override the default; puppetd needs this, usually.
+ # You can still override this on the command-line with, e.g., :compiler.
+ Puppet[:catalog_terminus] = :rest
+
+ Puppet[:facts_terminus] = :network_device
+
+ Puppet::Resource::Catalog.indirection.cache_class = :yaml
+ end
+end
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index 680762b94..dbd5a9437 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -487,6 +487,11 @@ module Puppet
This should match how often the hosts report back to the server."]
)
+ setdefaults(:device,
+ :devicedir => {:default => "$vardir/devices", :mode => "750", :desc => "The root directory of devices' $vardir"},
+ :deviceconfig => ["$confdir/device.conf","Path to the device config file for puppet device"]
+ )
+
setdefaults(:agent,
:localconfig => { :default => "$statedir/localconfig",
:owner => "root",
diff --git a/lib/puppet/indirector/facts/network_device.rb b/lib/puppet/indirector/facts/network_device.rb
new file mode 100644
index 000000000..c9bac803e
--- /dev/null
+++ b/lib/puppet/indirector/facts/network_device.rb
@@ -0,0 +1,25 @@
+require 'puppet/node/facts'
+require 'puppet/indirector/code'
+
+class Puppet::Node::Facts::NetworkDevice < Puppet::Indirector::Code
+ desc "Retrieve facts from a network device."
+
+ # Look a device's facts up through the current device.
+ def find(request)
+ result = Puppet::Node::Facts.new(request.key, Puppet::Util::NetworkDevice.current.facts)
+
+ result.add_local_facts
+ result.stringify
+ result.downcase_if_necessary
+
+ result
+ end
+
+ def destroy(facts)
+ raise Puppet::DevError, "You cannot destroy facts in the code store; it is only used for getting facts from a remote device"
+ end
+
+ def save(facts)
+ raise Puppet::DevError, "You cannot save facts to the code store; it is only used for getting facts from a remote device"
+ end
+end \ No newline at end of file
diff --git a/lib/puppet/provider/cisco.rb b/lib/puppet/provider/cisco.rb
new file mode 100644
index 000000000..918982f59
--- /dev/null
+++ b/lib/puppet/provider/cisco.rb
@@ -0,0 +1,9 @@
+require 'puppet/util/network_device/cisco/device'
+require 'puppet/provider/network_device'
+
+# This is the base class of all prefetched cisco device providers
+class Puppet::Provider::Cisco < Puppet::Provider::NetworkDevice
+ def self.device(url)
+ Puppet::Util::NetworkDevice::Cisco::Device.new(url)
+ end
+end
diff --git a/lib/puppet/provider/interface/cisco.rb b/lib/puppet/provider/interface/cisco.rb
index f3bd202e9..795a7f1ac 100644
--- a/lib/puppet/provider/interface/cisco.rb
+++ b/lib/puppet/provider/interface/cisco.rb
@@ -1,22 +1,20 @@
-require 'puppet/util/network_device/cisco/device'
-require 'puppet/provider/network_device'
+require 'puppet/provider/cisco'
-Puppet::Type.type(:interface).provide :cisco, :parent => Puppet::Provider::NetworkDevice do
+Puppet::Type.type(:interface).provide :cisco, :parent => Puppet::Provider::Cisco do
desc "Cisco switch/router provider for interface."
mk_resource_methods
- def self.lookup(url, name)
+ def self.lookup(device, name)
interface = nil
- network_gear = Puppet::Util::NetworkDevice::Cisco::Device.new(url)
- network_gear.command do |ng|
- interface = network_gear.interface(name)
+ device.command do |ng|
+ interface = device.interface(name)
end
interface
end
- def initialize(*args)
+ def initialize(device, *args)
super
end
@@ -26,8 +24,4 @@ Puppet::Type.type(:interface).provide :cisco, :parent => Puppet::Provider::Netwo
end
super
end
-
- def device
- @device ||= Puppet::Util::NetworkDevice::Cisco::Device.new(resource[:device_url])
- end
end
diff --git a/lib/puppet/provider/network_device.rb b/lib/puppet/provider/network_device.rb
index 58865fddc..b178df977 100644
--- a/lib/puppet/provider/network_device.rb
+++ b/lib/puppet/provider/network_device.rb
@@ -2,17 +2,22 @@
# This is the base class of all prefetched network device provider
class Puppet::Provider::NetworkDevice < Puppet::Provider
- def self.lookup(url, name)
+ def self.device(url)
+ raise "This provider doesn't implement the necessary device method"
+ end
+
+ def self.lookup(device, name)
raise "This provider doesn't implement the necessary lookup method"
end
def self.prefetch(resources)
resources.each do |name, resource|
- if result = lookup(resource[:device_url], name)
+ device = Puppet::Util::NetworkDevice.current || device(resource[:device_url])
+ if result = lookup(device, name)
result[:ensure] = :present
- resource.provider = new(result)
+ resource.provider = new(device, result)
else
- resource.provider = new(:ensure => :absent)
+ resource.provider = new(device, :ensure => :absent)
end
end
end
@@ -21,8 +26,12 @@ class Puppet::Provider::NetworkDevice < Puppet::Provider
@property_hash[:ensure] != :absent
end
- def initialize(*args)
- super
+ attr_accessor :device
+
+ def initialize(device, *args)
+ super(*args)
+
+ @device = device
# Make a duplicate, so that we have a copy for comparison
# at the end.
diff --git a/lib/puppet/provider/vlan/cisco.rb b/lib/puppet/provider/vlan/cisco.rb
index 46e172c73..3421d35b0 100644
--- a/lib/puppet/provider/vlan/cisco.rb
+++ b/lib/puppet/provider/vlan/cisco.rb
@@ -1,22 +1,20 @@
-require 'puppet/util/network_device/cisco/device'
-require 'puppet/provider/network_device'
+require 'puppet/provider/cisco'
-Puppet::Type.type(:vlan).provide :cisco, :parent => Puppet::Provider::NetworkDevice do
+Puppet::Type.type(:vlan).provide :cisco, :parent => Puppet::Provider::Cisco do
desc "Cisco switch/router provider for vlans."
mk_resource_methods
- def self.lookup(url, id)
+ def self.lookup(device, id)
vlans = {}
- device = Puppet::Util::NetworkDevice::Cisco::Device.new(url)
device.command do |d|
vlans = d.parse_vlans || {}
end
vlans[id]
end
- def initialize(*args)
+ def initialize(device, *args)
super
end
@@ -27,8 +25,4 @@ Puppet::Type.type(:vlan).provide :cisco, :parent => Puppet::Provider::NetworkDev
end
super
end
-
- def device
- @device ||= Puppet::Util::NetworkDevice::Cisco::Device.new(resource[:device_url])
- end
end
diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb
index 98c29657e..a6cff9bdc 100644
--- a/lib/puppet/resource/catalog.rb
+++ b/lib/puppet/resource/catalog.rb
@@ -133,6 +133,7 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
transaction.report = options[:report] if options[:report]
transaction.tags = options[:tags] if options[:tags]
transaction.ignoreschedules = true if options[:ignoreschedules]
+ transaction.for_network_device = options[:network_device]
transaction.add_times :config_retrieval => self.retrieval_duration || 0
diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb
index d7845fbc9..3728a2fff 100644
--- a/lib/puppet/transaction.rb
+++ b/lib/puppet/transaction.rb
@@ -12,7 +12,7 @@ class Puppet::Transaction
require 'puppet/transaction/resource_harness'
require 'puppet/resource/status'
- attr_accessor :component, :catalog, :ignoreschedules
+ attr_accessor :component, :catalog, :ignoreschedules, :for_network_device
attr_accessor :configurator
# The report, once generated.
@@ -339,6 +339,8 @@ class Puppet::Transaction
resource.warning "Skipping because of failed dependencies"
elsif resource.virtual?
resource.debug "Skipping because virtual"
+ elsif resource.appliable_to_device? ^ for_network_device
+ resource.debug "Skipping #{resource.appliable_to_device? ? 'device' : 'host'} resources because running on a #{for_network_device ? 'device' : 'host'}"
else
return false
end
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index c0e5d390b..656b8f264 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -116,6 +116,26 @@ class Type
ens
end
+ def self.apply_to_device
+ @apply_to = :device
+ end
+
+ def self.apply_to_host
+ @apply_to = :host
+ end
+
+ def self.apply_to_all
+ @apply_to = :both
+ end
+
+ def self.apply_to
+ @apply_to ||= :host
+ end
+
+ def self.can_apply_to(target)
+ [ target == :device ? :device : :host, :both ].include?(apply_to)
+ end
+
# Deal with any options passed into parameters.
def self.handle_param_options(name, options)
# If it's a boolean parameter, create a method to test the value easily
@@ -1895,6 +1915,14 @@ class Type
def virtual?; !!@virtual; end
def exported?; !!@exported; end
+
+ def appliable_to_device?
+ self.class.can_apply_to(:device)
+ end
+
+ def appliable_to_host?
+ self.class.can_apply_to(:host)
+ end
end
end
diff --git a/lib/puppet/type/interface.rb b/lib/puppet/type/interface.rb
index 7560a0552..d3b5cb06f 100644
--- a/lib/puppet/type/interface.rb
+++ b/lib/puppet/type/interface.rb
@@ -10,6 +10,8 @@ Puppet::Type.newtype(:interface) do
interface mode (access or trunking, native vlan and encapsulation),
switchport characteristics (speed, duplex)."
+ apply_to_device
+
ensurable do
defaultvalues
diff --git a/lib/puppet/type/schedule.rb b/lib/puppet/type/schedule.rb
index 5fb008f6f..f60f96fd2 100755
--- a/lib/puppet/type/schedule.rb
+++ b/lib/puppet/type/schedule.rb
@@ -43,6 +43,8 @@ module Puppet
This will cause resources to be applied every 30 minutes by default.
"
+ apply_to_all
+
newparam(:name) do
desc "The name of the schedule. This name is used to retrieve the
schedule when assigning it to an object:
diff --git a/lib/puppet/type/vlan.rb b/lib/puppet/type/vlan.rb
index 6708ea4f5..e39daf994 100644
--- a/lib/puppet/type/vlan.rb
+++ b/lib/puppet/type/vlan.rb
@@ -5,6 +5,8 @@
Puppet::Type.newtype(:vlan) do
@doc = "This represents a router or switch vlan."
+ apply_to_device
+
ensurable
newparam(:name) do
diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb
index a884b8658..714d03f74 100644
--- a/lib/puppet/util/command_line.rb
+++ b/lib/puppet/util/command_line.rb
@@ -14,7 +14,8 @@ module Puppet
'queue' => 'puppetqd',
'resource' => 'ralsh',
'kick' => 'puppetrun',
- 'master' => 'puppetmasterd'
+ 'master' => 'puppetmasterd',
+ 'device' => 'puppetdevice'
)
def initialize(zero = $0, argv = ARGV, stdin = STDIN)
diff --git a/lib/puppet/util/network_device.rb b/lib/puppet/util/network_device.rb
index bca66016b..d9c1aa34d 100644
--- a/lib/puppet/util/network_device.rb
+++ b/lib/puppet/util/network_device.rb
@@ -1,2 +1,12 @@
-module Puppet::Util::NetworkDevice
+class Puppet::Util::NetworkDevice
+ class << self
+ attr_reader :current
+ end
+
+ def self.init(device)
+ require "puppet/util/network_device/#{device.provider}/device"
+ @current = Puppet::Util::NetworkDevice.const_get(device.provider.capitalize).const_get(:Device).new(device.url)
+ rescue => detail
+ raise "Can't load #{device.provider} for #{device.name}: #{detail}"
+ end
end \ No newline at end of file
diff --git a/lib/puppet/util/network_device/base.rb b/lib/puppet/util/network_device/base.rb
index ff96c8693..7d6c3fc44 100644
--- a/lib/puppet/util/network_device/base.rb
+++ b/lib/puppet/util/network_device/base.rb
@@ -3,27 +3,25 @@ require 'uri'
require 'puppet/util/network_device/transport'
require 'puppet/util/network_device/transport/base'
-module Puppet::Util::NetworkDevice
- class Base
+class Puppet::Util::NetworkDevice::Base
- attr_accessor :url, :transport
+ attr_accessor :url, :transport
- def initialize(url)
- @url = URI.parse(url)
+ def initialize(url)
+ @url = URI.parse(url)
- @autoloader = Puppet::Util::Autoload.new(
- self,
- "puppet/util/network_device/transport",
- :wrap => false
- )
+ @autoloader = Puppet::Util::Autoload.new(
+ self,
+ "puppet/util/network_device/transport",
+ :wrap => false
+ )
- if @autoloader.load(@url.scheme)
- @transport = Puppet::Util::NetworkDevice::Transport.const_get(@url.scheme.capitalize).new
- @transport.host = @url.host
- @transport.port = @url.port || case @url.scheme ; when "ssh" ; 22 ; when "telnet" ; 23 ; end
- @transport.user = @url.user
- @transport.password = @url.password
- end
+ if @autoloader.load(@url.scheme)
+ @transport = Puppet::Util::NetworkDevice::Transport.const_get(@url.scheme.capitalize).new
+ @transport.host = @url.host
+ @transport.port = @url.port || case @url.scheme ; when "ssh" ; 22 ; when "telnet" ; 23 ; end
+ @transport.user = @url.user
+ @transport.password = @url.password
end
end
end \ No newline at end of file
diff --git a/lib/puppet/util/network_device/cisco/device.rb b/lib/puppet/util/network_device/cisco/device.rb
index 1f350991a..005470e13 100644
--- a/lib/puppet/util/network_device/cisco/device.rb
+++ b/lib/puppet/util/network_device/cisco/device.rb
@@ -3,6 +3,7 @@ require 'puppet/util'
require 'puppet/util/network_device/base'
require 'puppet/util/network_device/ipcalc'
require 'puppet/util/network_device/cisco/interface'
+require 'puppet/util/network_device/cisco/facts'
require 'ipaddr'
class Puppet::Util::NetworkDevice::Cisco::Device < Puppet::Util::NetworkDevice::Base
@@ -91,6 +92,15 @@ class Puppet::Util::NetworkDevice::Cisco::Device < Puppet::Util::NetworkDevice::
interface
end
+ def facts
+ @facts ||= Puppet::Util::NetworkDevice::Cisco::Facts.new(transport)
+ facts = {}
+ command do |ng|
+ facts = @facts.retrieve
+ end
+ facts
+ end
+
def interface(name)
ifname = canonalize_ifname(name)
interface = parse_interface(ifname)
diff --git a/lib/puppet/util/network_device/cisco/facts.rb b/lib/puppet/util/network_device/cisco/facts.rb
new file mode 100644
index 000000000..40e2b37c2
--- /dev/null
+++ b/lib/puppet/util/network_device/cisco/facts.rb
@@ -0,0 +1,72 @@
+
+require 'puppet/util/network_device/cisco'
+require 'puppet/util/network_device/ipcalc'
+
+# this retrieves facts from a cisco device
+class Puppet::Util::NetworkDevice::Cisco::Facts
+
+ attr_reader :transport
+
+ def initialize(transport)
+ @transport = transport
+ end
+
+ def retrieve
+ facts = {}
+ facts.merge(parse_show_ver)
+ end
+
+ def parse_show_ver
+ facts = {}
+ out = @transport.command("sh ver")
+ lines = out.split("\n")
+ lines.shift; lines.pop
+ lines.each do |l|
+ case l
+ # cisco WS-C2924C-XL (PowerPC403GA) processor (revision 0x11) with 8192K/1024K bytes of memory.
+ # Cisco 1841 (revision 5.0) with 355328K/37888K bytes of memory.
+ # Cisco 877 (MPC8272) processor (revision 0x200) with 118784K/12288K bytes of memory.
+ # cisco WS-C2960G-48TC-L (PowerPC405) processor (revision C0) with 61440K/4088K bytes of memory.
+ # cisco WS-C2950T-24 (RC32300) processor (revision R0) with 19959K bytes of memory.
+ when /[cC]isco ([\w-]+) (?:\(([\w-]+)\) processor )?\(revision (.+)\) with (\d+[KMG])(?:\/(\d+[KMG]))? bytes of memory\./
+ facts[:hardwaremodel] = $1
+ facts[:processor] = $2 if $2
+ facts[:hardwarerevision] = $3
+ facts[:memorysize] = $4
+ # uptime
+ # Switch uptime is 1 year, 12 weeks, 6 days, 22 hours, 32 minutes
+ # c2950 uptime is 3 weeks, 1 day, 23 hours, 36 minutes
+ # c2960 uptime is 2 years, 27 weeks, 5 days, 21 hours, 30 minutes
+ # router uptime is 5 weeks, 1 day, 3 hours, 30 minutes
+ when /^\s*([\w-]+)\s+uptime is (.*?)$/
+ facts[:hostname] = $1
+ facts[:uptime] = $2
+ facts[:uptime_seconds] = uptime_to_seconds($2)
+ facts[:uptime_days] = facts[:uptime_seconds] / 86400
+ # "IOS (tm) C2900XL Software (C2900XL-C3H2S-M), Version 12.0(5)WC10, RELEASE SOFTWARE (fc1)"=> { :operatingsystem => "IOS", :operatingsystemrelease => "12.0(5)WC10", :operatingsystemmajrelease => "12.0", :operatingsystemfeature => "C3H2S"},
+ # "IOS (tm) C2950 Software (C2950-I6K2L2Q4-M), Version 12.1(22)EA8a, RELEASE SOFTWARE (fc1)"=> { :operatingsystem => "IOS", :operatingsystemrelease => "12.1(22)EA8a", :operatingsystemmajrelease => "12.1", :operatingsystemfeature => "I6K2L2Q4"},
+ # "Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 12.2(44)SE, RELEASE SOFTWARE (fc1)"=>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.2(44)SE", :operatingsystemmajrelease => "12.2", :operatingsystemfeature => "LANBASEK9"},
+ # "Cisco IOS Software, C870 Software (C870-ADVIPSERVICESK9-M), Version 12.4(11)XJ4, RELEASE SOFTWARE (fc2)"=>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.4(11)XJ40", :operatingsystemmajrelease => "12.4XJ", :operatingsystemfeature => "ADVIPSERVICESK9"},
+ # "Cisco IOS Software, 1841 Software (C1841-ADVSECURITYK9-M), Version 12.4(24)T4, RELEASE SOFTWARE (fc2)" =>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.4(24)T4", :operatingsystemmajrelease => "12.4T", :operatingsystemfeature => "ADVSECURITYK9"},
+ when /(?:Cisco )?(IOS)\s*(?:\(tm\) |Software, )?(?:\w+)\s+Software\s+\(\w+-(\w+)-\w+\), Version ([0-9.()A-Za-z]+),/
+ facts[:operatingsystem] = $1
+ facts[:operatingsystemrelease] = $3
+ facts[:operatingsystemmajrelease] = ios_major_version(facts[:operatingsystemrelease])
+ facts[:operatingsystemfeature] = $2
+ end
+ end
+ facts
+ end
+
+ def ios_major_version(version)
+ version.gsub(/^(\d+)\.(\d+)\(.+\)([A-Z]+)([\da-z]+)?/, '\1.\2\3')
+ end
+
+ def uptime_to_seconds(uptime)
+ captures = (uptime.match /^(?:(?:(?:(?:(\d+) years?,)?\s*(\d+) weeks?,)?\s*(\d+) days?,)?\s*(\d+) hours?,)?\s*(\d+) minutes?$/).captures
+ seconds = captures.zip([31536000, 604800, 86400, 3600, 60]).inject(0) do |total, (x,y)|
+ total + (x.nil? ? 0 : x.to_i * y)
+ end
+ end
+
+end \ No newline at end of file
diff --git a/lib/puppet/util/network_device/config.rb b/lib/puppet/util/network_device/config.rb
new file mode 100644
index 000000000..17f4e254c
--- /dev/null
+++ b/lib/puppet/util/network_device/config.rb
@@ -0,0 +1,93 @@
+require 'ostruct'
+require 'puppet/util/loadedfile'
+
+class Puppet::Util::NetworkDevice::Config < Puppet::Util::LoadedFile
+
+ def self.main
+ @main ||= self.new
+ end
+
+ def self.devices
+ main.devices || []
+ end
+
+ attr_reader :devices
+
+ def exists?
+ FileTest.exists?(@file)
+ end
+
+ def initialize()
+ @file = Puppet[:deviceconfig]
+
+ raise Puppet::DevError, "No device config file defined" unless @file
+ return unless self.exists?
+ super(@file)
+ @devices = {}
+
+ read(true) # force reading at start
+ end
+
+ # Read the configuration file.
+ def read(force = false)
+ return unless FileTest.exists?(@file)
+
+ parse if force or changed?
+ end
+
+ private
+
+ def parse
+ begin
+ devices = {}
+ device = nil
+ File.open(@file) { |f|
+ count = 1
+ f.each { |line|
+ case line
+ when /^\s*#/ # skip comments
+ count += 1
+ next
+ when /^\s*$/ # skip blank lines
+ count += 1
+ next
+ when /^\[([\w.]+)\]\s*$/ # [device.fqdn]
+ name = $1
+ name.chomp!
+ raise ConfigurationError, "Duplicate device found at line #{count}, already found at #{device.line}" if devices.include?(name)
+ device = OpenStruct.new
+ device.name = name
+ device.line = count
+ Puppet.debug "found device: #{device.name} at #{device.line}"
+ devices[name] = device
+ when /^\s*(type|url)\s+(.+)$/
+ parse_directive(device, $1, $2, count)
+ else
+ raise ConfigurationError, "Invalid line #{count}: #{line}"
+ end
+ count += 1
+ }
+ }
+ rescue Errno::EACCES => detail
+ Puppet.err "Configuration error: Cannot read #{@file}; cannot serve"
+ #raise Puppet::Error, "Cannot read #{@config}"
+ rescue Errno::ENOENT => detail
+ Puppet.err "Configuration error: '#{@file}' does not exit; cannot serve"
+ end
+
+ @devices = devices
+ end
+
+ def parse_directive(device, var, value, count)
+ case var
+ when "type"
+ device.provider = value
+ when "url"
+ device.url = value
+ else
+ raise ConfigurationError,
+ "Invalid argument '#{var}' at line #{count}"
+ end
+ end
+
+end \ No newline at end of file
diff --git a/lib/puppet/util/network_device/transport.rb b/lib/puppet/util/network_device/transport.rb
index e64fe9b69..cef8f3859 100644
--- a/lib/puppet/util/network_device/transport.rb
+++ b/lib/puppet/util/network_device/transport.rb
@@ -1,5 +1,3 @@
# stub
-module Puppet::Util::NetworkDevice
- module Transport
- end
+module Puppet::Util::NetworkDevice::Transport
end \ No newline at end of file
diff --git a/lib/puppet/util/network_device/transport/ssh.rb b/lib/puppet/util/network_device/transport/ssh.rb
index b3cf51b8a..bf0e7193c 100644
--- a/lib/puppet/util/network_device/transport/ssh.rb
+++ b/lib/puppet/util/network_device/transport/ssh.rb
@@ -31,6 +31,10 @@ class Puppet::Util::NetworkDevice::Transport::Ssh < Puppet::Util::NetworkDevice:
@ssh = Net::SSH.start(host, user, :port => port, :password => password, :timeout => timeout)
rescue TimeoutError
raise TimeoutError, "timed out while opening an ssh connection to the host"
+ rescue Net::SSH::AuthenticationFailed
+ raise Puppet::Error, "SSH authentication failure connecting to #{host} as #{user}"
+ rescue Net::SSH::Exception => detail
+ raise Puppet::Error, "SSH connection failure to #{host}"
end
@buf = ""