PageRenderTime 35ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/puppet/util/network_device/cisco/device.rb

https://github.com/david-caro/puppet
Ruby | 267 lines | 253 code | 13 blank | 1 comment | 16 complexity | 5f3ea04729546f2ae7666583ec8b4471 MD5 | raw file
Possible License(s): Apache-2.0, CC-BY-3.0
  1. require 'puppet'
  2. require 'puppet/util'
  3. require 'puppet/util/network_device/base'
  4. require 'puppet/util/network_device/ipcalc'
  5. require 'puppet/util/network_device/cisco/interface'
  6. require 'puppet/util/network_device/cisco/facts'
  7. require 'ipaddr'
  8. class Puppet::Util::NetworkDevice::Cisco::Device < Puppet::Util::NetworkDevice::Base
  9. include Puppet::Util::NetworkDevice::IPCalc
  10. attr_accessor :enable_password
  11. def initialize(url, options = {})
  12. super(url, options)
  13. @enable_password = options[:enable_password] || parse_enable(@url.query)
  14. transport.default_prompt = /[#>]\s?\z/n
  15. end
  16. def parse_enable(query)
  17. if query
  18. params = CGI.parse(query)
  19. params['enable'].first unless params['enable'].empty?
  20. end
  21. end
  22. def connect
  23. transport.connect
  24. login
  25. transport.command("terminal length 0") do |out|
  26. enable if out =~ />\s?\z/n
  27. end
  28. find_capabilities
  29. end
  30. def disconnect
  31. transport.close
  32. end
  33. def command(cmd = nil)
  34. connect
  35. out = execute(cmd) if cmd
  36. yield self if block_given?
  37. disconnect
  38. out
  39. end
  40. def execute(cmd)
  41. transport.command(cmd)
  42. end
  43. def login
  44. return if transport.handles_login?
  45. if @url.user != ''
  46. transport.command(@url.user, :prompt => /^Password:/)
  47. else
  48. transport.expect(/^Password:/)
  49. end
  50. transport.command(@url.password)
  51. end
  52. def enable
  53. raise "Can't issue \"enable\" to enter privileged, no enable password set" unless enable_password
  54. transport.command("enable", :prompt => /^Password:/)
  55. transport.command(enable_password)
  56. end
  57. def support_vlan_brief?
  58. !! @support_vlan_brief
  59. end
  60. def find_capabilities
  61. out = execute("sh vlan brief")
  62. lines = out.split("\n")
  63. lines.shift; lines.pop
  64. @support_vlan_brief = ! (lines.first =~ /^%/)
  65. end
  66. IF = {
  67. :FastEthernet => %w{FastEthernet FastEth Fast FE Fa F},
  68. :GigabitEthernet => %w{GigabitEthernet GigEthernet GigEth GE Gi G},
  69. :TenGigabitEthernet => %w{TenGigabitEthernet TE Te},
  70. :Ethernet => %w{Ethernet Eth E},
  71. :Serial => %w{Serial Se S},
  72. :PortChannel => %w{PortChannel Port-Channel Po},
  73. :POS => %w{POS P},
  74. :VLAN => %w{VLAN VL V},
  75. :Loopback => %w{Loopback Loop Lo},
  76. :ATM => %w{ATM AT A},
  77. :Dialer => %w{Dialer Dial Di D},
  78. :VirtualAccess => %w{Virtual-Access Virtual-A Virtual Virt}
  79. }
  80. def canonalize_ifname(interface)
  81. IF.each do |k,ifnames|
  82. if found = ifnames.find { |ifname| interface =~ /^#{ifname}\s*\d/i }
  83. found = /^#{found}(.+)\Z/i.match(interface)
  84. return "#{k.to_s}#{found[1]}".gsub(/\s+/,'')
  85. end
  86. end
  87. interface
  88. end
  89. def facts
  90. @facts ||= Puppet::Util::NetworkDevice::Cisco::Facts.new(transport)
  91. facts = {}
  92. command do |ng|
  93. facts = @facts.retrieve
  94. end
  95. facts
  96. end
  97. def interface(name)
  98. ifname = canonalize_ifname(name)
  99. interface = parse_interface(ifname)
  100. return { :ensure => :absent } if interface.empty?
  101. interface.merge!(parse_trunking(ifname))
  102. interface.merge!(parse_interface_config(ifname))
  103. end
  104. def new_interface(name)
  105. Puppet::Util::NetworkDevice::Cisco::Interface.new(canonalize_ifname(name), transport)
  106. end
  107. def parse_interface(name)
  108. resource = {}
  109. out = execute("sh interface #{name}")
  110. lines = out.split("\n")
  111. lines.shift; lines.pop
  112. lines.each do |l|
  113. if l =~ /#{name} is (.+), line protocol is /
  114. resource[:ensure] = ($1 == 'up' ? :present : :absent);
  115. end
  116. if l =~ /Auto Speed \(.+\),/ or l =~ /Auto Speed ,/ or l =~ /Auto-speed/
  117. resource[:speed] = :auto
  118. end
  119. if l =~ /, (.+)Mb\/s/
  120. resource[:speed] = $1
  121. end
  122. if l =~ /\s+Auto-duplex \((.{4})\),/
  123. resource[:duplex] = :auto
  124. end
  125. if l =~ /\s+(.+)-duplex/
  126. resource[:duplex] = $1 == "Auto" ? :auto : $1.downcase.to_sym
  127. end
  128. if l =~ /Description: (.+)/
  129. resource[:description] = $1
  130. end
  131. end
  132. resource
  133. end
  134. def parse_interface_config(name)
  135. resource = Hash.new { |hash, key| hash[key] = Array.new ; }
  136. out = execute("sh running-config interface #{name} | begin interface")
  137. lines = out.split("\n")
  138. lines.shift; lines.pop
  139. lines.each do |l|
  140. if l =~ /ip address (#{IP}) (#{IP})\s+secondary\s*$/
  141. resource[:ipaddress] << [prefix_length(IPAddr.new($2)), IPAddr.new($1), 'secondary']
  142. end
  143. if l =~ /ip address (#{IP}) (#{IP})\s*$/
  144. resource[:ipaddress] << [prefix_length(IPAddr.new($2)), IPAddr.new($1), nil]
  145. end
  146. if l =~ /ipv6 address (#{IP})\/(\d+) (eui-64|link-local)/
  147. resource[:ipaddress] << [$2.to_i, IPAddr.new($1), $3]
  148. end
  149. if l =~ /channel-group\s+(\d+)/
  150. resource[:etherchannel] = $1
  151. end
  152. end
  153. resource
  154. end
  155. def parse_vlans
  156. vlans = {}
  157. out = execute(support_vlan_brief? ? "sh vlan brief" : "sh vlan-switch brief")
  158. lines = out.split("\n")
  159. lines.shift; lines.shift; lines.shift; lines.pop
  160. vlan = nil
  161. lines.each do |l|
  162. case l
  163. # vlan name status
  164. when /^(\d+)\s+(\w+)\s+(\w+)\s+([a-zA-Z0-9,\/. ]+)\s*$/
  165. vlan = { :name => $1, :description => $2, :status => $3, :interfaces => [] }
  166. if $4.strip.length > 0
  167. vlan[:interfaces] = $4.strip.split(/\s*,\s*/).map{ |ifn| canonalize_ifname(ifn) }
  168. end
  169. vlans[vlan[:name]] = vlan
  170. when /^\s+([a-zA-Z0-9,\/. ]+)\s*$/
  171. raise "invalid sh vlan summary output" unless vlan
  172. if $1.strip.length > 0
  173. vlan[:interfaces] += $1.strip.split(/\s*,\s*/).map{ |ifn| canonalize_ifname(ifn) }
  174. end
  175. else
  176. end
  177. end
  178. vlans
  179. end
  180. def update_vlan(id, is = {}, should = {})
  181. if should[:ensure] == :absent
  182. Puppet.info "Removing #{id} from device vlan"
  183. execute("conf t")
  184. execute("no vlan #{id}")
  185. execute("exit")
  186. return
  187. end
  188. # We're creating or updating an entry
  189. execute("conf t")
  190. execute("vlan #{id}")
  191. [is.keys, should.keys].flatten.uniq.each do |property|
  192. Puppet.debug("trying property: #{property}: #{should[property]}")
  193. next if property != :description
  194. execute("name #{should[property]}")
  195. end
  196. execute("exit")
  197. execute("exit")
  198. end
  199. def parse_trunking(interface)
  200. trunking = {}
  201. out = execute("sh interface #{interface} switchport")
  202. lines = out.split("\n")
  203. lines.shift; lines.pop
  204. lines.each do |l|
  205. case l
  206. when /^Administrative mode:\s+(.*)$/i
  207. case $1
  208. when "trunk"
  209. trunking[:mode] = :trunk
  210. when "static access"
  211. trunking[:mode] = :access
  212. else
  213. raise "Unknown switchport mode: #{$1} for #{interface}"
  214. end
  215. when /^Administrative Trunking Encapsulation:\s+(.*)$/
  216. case $1
  217. when "dot1q","isl"
  218. trunking[:encapsulation] = $1.to_sym if trunking[:mode] == :trunk
  219. else
  220. raise "Unknown switchport encapsulation: #{$1} for #{interface}"
  221. end
  222. when /^Access Mode VLAN:\s+(.*) \(\(Inactive\)\)$/
  223. # nothing
  224. when /^Access Mode VLAN:\s+(.*) \(.*\)$/
  225. trunking[:native_vlan] = $1 if trunking[:mode] == :access
  226. when /^Trunking VLANs Enabled:\s+(.*)$/
  227. next if trunking[:mode] == :access
  228. vlans = $1
  229. trunking[:allowed_trunk_vlans] = case vlans
  230. when /all/i
  231. :all
  232. when /none/i
  233. :none
  234. else
  235. vlans
  236. end
  237. end
  238. end
  239. trunking
  240. end
  241. end