Bug #12614

Possible problem in util/network_device/transport/telnet.rb

Added by Gary Richards over 1 year ago. Updated 4 months ago.

Status:AcceptedStart date:02/13/2012
Priority:NormalDue date:
Assignee:-% Done:

0%

Category:network
Target version:-
Affected Puppet version:2.7.10 Branch:
Keywords:

Description

Hi,

I think there’s a bug in util/network_device/transport/telnet.rb

I’ve been trying to modify a copy of the existing cisco network code work with HP Procurve switches and spent a little while trying to work out why I couldn’t get the enable part to work.

in util/network_device/cisco/device.rb, in the enable method:

transport.command("enable", :prompt => /^Password:/)

It just so happens the switch I was using to test my changes doesn’t have an enable password. So when you type ‘enable’ you get dumped back into slightly changed prompt.

Now looking at the code in util/network_device/transport/telnet.rb

def command(cmd, options = {})
  send(cmd)
  expect(options[:prompt] || default_prompt) do |output|
    yield output if block_given?
  end
end

I think that’s supposed to expect either the passed in prompt or the default prompt.

Now as suggested, the code that calls enable expects a prompt of Password: so looking at the above code, that should match Password: or the default prompt. So according to my calculations… that should mean that my switch, which doesn’t require a password be entered should work with the same code. But it doesn’t seem to work.

The prompt is never matched. I modified the code in util/network_device/cisco/device.rb, in the enable method by removing the prompt (to make sure that it only matches the default prompt):

transport.command("enable")

Lo and behold it matches the default prompt and the enable method works fine.

That suggests to me that the

options[:prompt] || default_prompt

Part of the code doesn’t work however the original developer expected?

I guess it’s possible that on cisco devices it’s not possible to have no password for enable (I have no cisco device to check on)? Regardless of that, it’s possible this code may be used some somewhere else either now or in the future where someone expects it to match a specific prompt or the default prompt and it might not work?

12614_with_spec.patch Magnifier (2.9 KB) Chris Price, 02/17/2012 10:25 am


Related issues

Related to Puppet - Bug #12835: Cisco device, interface type, ensure => absent won't work... Accepted 02/27/2012

History

#1 Updated by Chris Price over 1 year ago

  • File 12614.patch added
  • Status changed from Unreviewed to Accepted

Spoke with Daniel and Nan about this ticket; we are all in agreement that changing the code to be tolerant of either of the two prompts sounds like a valid solution. However, we don’t have a particularly easy way to test this against the range of devices that it could be in use on.

The code:

expect(options[:prompt] || default_prompt)

is intended to only match one specific prompt; it prefers the one explicitly passed in via “options” if provided, and falls back to the default_prompt otherwise.

The “expect” method does support regular expressions, though, so this code could probably be changed to use a regular expression that would match either prompt.

I’ve attached a patch against 2.7.10 that might accomplish this; it’s just a stab in the dark, hasn’t been tested at all other than to confirm it doesn’t break our existing unit tests. In order to consider merging it into the main codebase we’d probably need to add a few tests to telnet_spec.rb and also to test this against a few devices.

Gary, if you are able to apply this patch, test it against your device, and let us know how it goes—that would be very helpful information.

#2 Updated by Gary Richards over 1 year ago

Something’s not quite right with the patch

I added a couple of debug statements to try and diagnose:

if (options.has_key?(:prompt)) then
  Puppet.debug("options prompt: #{options[:prompt]}")
  Puppet.debug("default prompt: #{default_prompt}")
  # if we've been explicitly passed an alternate prompt, we will build up a regular expression that
  # can match either the specified prompt or the default prompt
  prompt_regex = /^(?:#{Regexp.escape(options[:prompt])})|(?:#{Regexp.escape(default_prompt)})$/
  Puppet.debug("Prompt regex: #{prompt_regex}")
  # Net::Telnet.waitfor expects a hash if you want to use a regex to match the prompt:
  prompt = { "Match" => prompt_regex }
  Puppet.debug("prompt: #{prompt}")
end

The output I get is:

debug: options prompt: (?-mix:Password:)
debug: default prompt: (?-mix:[a-zA-Z0-9\-_. ]*[#>]\s?)
err: Could not send report: No route to host - connect(2)
err: Could not retrieve local facts: can't convert Regexp into String

I’m slightly confused as to why I don’t see the other debug output as i’m sure the code has to get as far as the expect call before it will timeout?!

I even commented out my own code thinking that perhaps it was somehow the cause of ‘can’t convert Regexp into String’, but it doesn’t seem to be.

#3 Updated by Chris Price over 1 year ago

  • File deleted (12614.patch)

#4 Updated by Chris Price over 1 year ago

You are getting that error from the “Regexp.escape” call—that method expects a String, but the prompt objects that you printed out are Regexp objects.

Here’s an updated patch (still based on 2.7.10) that includes a simple (spec) unit test… hopefully should fix the specific error you were seeing.

#5 Updated by Gary Richards over 1 year ago

That seems to do it. At least, in my fairly limited testing :)

Thanks

Gary

#6 Updated by Chris Price over 1 year ago

Awesome, thanks for the feedback.

Can you post a bit of info about what kind(s) of device(s) you’ve tested against?

Then I think we will need to get a bit of testing on some Cisco devices, and at that point this patch can be considered for integration into the main codebase.

#7 Updated by Gary Richards over 1 year ago

Right now i’m just trying to talk to a HP ProCurve 2510G-48 L2 managed switch and get some facts from it. Then I hope to implement vlans and interfaces similar to the existing Cisco code. I also have some HP E4800 L3 managed switches (they’re actually non rebranded 3com’s, which are rebranded H3C S5500 switches) which I hope to implement similar functionality for at some point (but they’re less important).

The only reason for the above request was due to the switch i’m testing on. In its default config (which is how we hope to one day be able to provision a new switch) once you’re connected, if you type enable you don’t get prompted for a password. So the existing puppet code fell over at the first hurdle as there was no easy way to say, execute ‘enable’, expect Password: or expect the default prompt.

One thing we’ve been thinking that we might like to do (in the future) is to have puppet configure switch ports on the fly based on a few things. For example, it would be nice if we could define a virtual machine to be on a certain host. As part of the VM config you would tell puppet which vlans its interfaces needed to be connected to. Then using various puppet magic, the VM config would configure various switch ports to be configured correctly for whichever vlans are required by VMs on a specific host. This sort of thing may be a little way off, but it’s quite important to us at least initially to be able to configure vlans on our switches manually via puppet and we can’t do that until puppet can actually manage vlans on our switches. So this is something i’m vaguely working on whenever time allows.

#8 Updated by Chris Price over 1 year ago

Thanks for all of the extra info, that is helpful.

#9 Updated by Gary Richards over 1 year ago

As i’m slowly getting further with my project to get puppet working with my own switches, I wonder if I jumped the gun on this request due to not understanding how the code worked.

As I had assumed that both the default prompt and the prompt passed to the command method should match in the output, I based my thinking around that assumption and therefore this ticket turned into more of a request for that ability rather than fully understanding what was actually happening and why it might only make sense to match against the specific prompt specified.

I realise now that there may be a situation where you would try to expect(/Password:/) or something and you really would only ever want to match on that output NEVER on the default prompt. If you received the default prompt it might actually mean that something went wrong and that you don’t want to continue.

I also realise that due to my misunderstanding of how the code worked, I had assumed that this underlying code needed to be changed to be ‘correct’ when actually it was correct and that once it was explained above how this code works (ie. it’s not a match this or match that, it’s match this if it’s not otherwise match that), I should have realised that making my switch code work this way and expect very specific things makes far far more sense than expecting something or the default prompt.

That said, I can see that there may be instances where you might want to match a prompt or the default prompt. So perhaps this functionality could somehow be changed so that by default it works how it originally worked, but also have a way to tell it to work in a way that the patch allows. Essentially supporting both, but defaulting to how it originally worked.

If I find time, perhaps i’ll be able to provide such a patch myself!

Anyhow, cutting a long post short, this patch as it is now probably should NOT end up merged into the mainline code.

#10 Updated by Paul Tötterman 4 months ago

I’m pretty sure this isn’t the place to ask, but I couldn’t find a better place, so:

Gary, how is management of ProCurve devices progressing? Do you have something you could share for testing?

Also available in: Atom PDF