Skip to content

Commit

Permalink
Added ability to specify remote port for NetDevice objects
Browse files Browse the repository at this point in the history
fixes #172

+ Bugfix when passwords are passed in to make sure they are not unicode
+ Add defaults in settings.py for SSH (SSH_PORT) and Telnet (SSH_TELNET) ports
+ Added documentation for SSH_PORT and TELNET_PORT in settings.py
  • Loading branch information
jathanism committed Jun 10, 2014
1 parent ce0f685 commit b1daa08
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 11 deletions.
6 changes: 6 additions & 0 deletions conf/trigger_settings.py
Expand Up @@ -148,6 +148,12 @@
# Whether or not to allow telnet fallback
TELNET_ENABLED = True

# Default ports for SSH
SSH_PORT = 22

# Default port for Telnet
TELNET_PORT = 23

# A mapping of vendors to the types of devices for that vendor for which you
# would like to disable interactive (pty) SSH sessions, such as when using
# bin/gong.
Expand Down
26 changes: 26 additions & 0 deletions docs/usage/configuration.rst
Expand Up @@ -360,6 +360,32 @@ Default::

True

.. setting:: SSH_PORT

SSH_PORT
~~~~~~~~

.. versionadded:: 1.4.4

Destination TCP port to use for SSH client connections.

Default::

22

.. setting:: TELNET_PORT

TELNET_PORT
~~~~~~~~~~~

.. versionadded:: 1.4.4

Destination TCP port to use for Telnet client connections.

Default::

23

.. setting:: TRIGGER_ENABLEPW

TRIGGER_ENABLEPW
Expand Down
2 changes: 1 addition & 1 deletion trigger/__init__.py
@@ -1,4 +1,4 @@
__version__ = (1, 4, 3, 'r5')
__version__ = (1, 4, 3, 'r6')

full_version = '.'.join(map(str, __version__[0:3])) + ''.join(__version__[3:])
release = full_version
Expand Down
6 changes: 6 additions & 0 deletions trigger/conf/global_settings.py
Expand Up @@ -160,6 +160,12 @@
# Whether or not to allow telnet fallback
TELNET_ENABLED = True

# Default ports for SSH
SSH_PORT = 22

# Default port for Telnet
TELNET_PORT = 23

# A mapping of vendors to the types of devices for that vendor for which you
# would like to disable interactive (pty) SSH sessions, such as when using
# bin/gong.
Expand Down
33 changes: 31 additions & 2 deletions trigger/netdevices/__init__.py
Expand Up @@ -26,7 +26,7 @@
__maintainer__ = 'Jathan McCollum'
__email__ = 'jathan.mccollum@teamaol.com'
__copyright__ = 'Copyright 2006-2013, AOL Inc.; 2013 Salesforce.com'
__version__ = '2.2.2'
__version__ = '2.3'

# Imports
import copy
Expand All @@ -36,7 +36,7 @@
import time
from twisted.python import log
from trigger.conf import settings
from trigger.utils import network
from trigger.utils import network, parse_node_port
from trigger.utils.url import parse_url
from trigger import changemgmt, exceptions, rancid
from UserDict import DictMixin
Expand Down Expand Up @@ -182,6 +182,7 @@ def __init__(self, data=None, with_acls=None):

# Hostname
self.nodeName = None
self.nodePort = None

# Hardware Info
self.deviceType = None
Expand All @@ -196,6 +197,7 @@ def __init__(self, data=None, with_acls=None):
self.assetID = None
self.budgetCode = None
self.budgetName = None
self.enablePW = None
self.owningTeam = None
self.owner = None
self.onCallName = None
Expand All @@ -213,6 +215,9 @@ def __init__(self, data=None, with_acls=None):
if data is not None:
self._populate_data(data)

# Set node remote port based on "hostname:port" as nodeName
self._set_node_port()

# Cleanup the attributes (strip whitespace, lowercase values, etc.)
self._cleanup_attributes()

Expand Down Expand Up @@ -263,6 +268,10 @@ def _cleanup_attributes(self):
if self.deviceType is not None:
self.deviceType = self.deviceType.upper()

# Make sure the password is bytes not unicode
if self.enablePW is not None:
self.enablePW = str(self.enablePW)

# Cleanup whitespace from owning team
if self.owningTeam is not None:
self.owningTeam = self.owningTeam.strip()
Expand All @@ -275,6 +284,26 @@ def _cleanup_attributes(self):
}
self.adminStatus = STATUS_MAP.get(self.deviceStatus, STATUS_MAP['up'])

def _set_node_port(self):
"""Set the freakin' TCP port"""
# If nodename is set, try to parse out a nodePort
if self.nodeName is not None:
nodeport_info = parse_node_port(self.nodeName)
nodeName, nodePort = nodeport_info

# If the nodeName differs, use it to replace the one we parsed
if nodeName != self.nodeName:
self.nodeName = nodeName

# If the port isn't set, set it
if nodePort is not None:
self.nodePort = nodePort
return None

# Make sure the port is an integer if it's not None
if self.nodePort is not None and isinstance(self.nodePort, basestring):
self.nodePort = int(self.nodePort)

def _populate_deviceType(self):
"""Try to make a guess what the device type is"""
self.deviceType = settings.DEFAULT_TYPES.get(self.vendor.name,
Expand Down
19 changes: 11 additions & 8 deletions trigger/twister.py
Expand Up @@ -9,6 +9,7 @@
__maintainer__ = 'Jathan McCollum'
__email__ = 'jathan.mccollum@teamaol.com'
__copyright__ = 'Copyright 2006-2013, AOL Inc.; 2013 Salesforce.com'
__version__ = '1.5.6'

import copy
import fcntl
Expand Down Expand Up @@ -216,17 +217,17 @@ def pty_connect(device, action, creds=None, display_banner=None,

factory = TriggerSSHPtyClientFactory(d, action, creds, display_banner,
init_commands, device=device)
log.msg('Trying SSH to %s' % device, debug=True)
port = 22
port = device.nodePort or settings.SSH_PORT
log.msg('Trying SSH to %s:%s' % (device, port), debug=True)

# or Telnet?
elif settings.TELNET_ENABLED:
log.msg('[%s] SSH connection test FAILED, falling back to telnet' %
device)
factory = TriggerTelnetClientFactory(d, action, creds,
init_commands=init_commands, device=device)
log.msg('Trying telnet to %s' % device, debug=True)
port = 23
port = device.nodePort or settings.TELNET_PORT
log.msg('Trying telnet to %s:%s' % (device, port), debug=True)
else:
log.msg('[%s] SSH connection test FAILED, telnet fallback disabled' % device)
return None
Expand Down Expand Up @@ -444,8 +445,9 @@ def execute_generic_ssh(device, commands, creds=None, incremental=None,
command_interval, prompt_pattern,
device, connection_class)

log.msg('Trying %s SSH to %s' % (method, device), debug=True)
reactor.connectTCP(device.nodeName, 22, factory)
port = device.nodePort or settings.SSH_PORT
log.msg('Trying %s SSH to %s:%s' % (method, device, port), debug=True)
reactor.connectTCP(device.nodeName, port, factory)
return d

def execute_exec_ssh(device, commands, creds=None, incremental=None,
Expand Down Expand Up @@ -542,8 +544,9 @@ def execute_ioslike_telnet(device, commands, creds=None, incremental=None,
timeout, command_interval)
factory = TriggerTelnetClientFactory(d, action, creds, loginpw, enablepw)

log.msg('Trying IOS-like scripting to %s' % device, debug=True)
reactor.connectTCP(device.nodeName, 23, factory)
port = device.nodePort or settings.TELNET_PORT
log.msg('Trying IOS-like scripting to %s:%s' % (device, port), debug=True)
reactor.connectTCP(device.nodeName, port, factory)
return d

def execute_async_pty_ssh(device, commands, creds=None, incremental=None,
Expand Down
14 changes: 14 additions & 0 deletions trigger/utils/__init__.py
Expand Up @@ -62,3 +62,17 @@ def strip_juniper_namespace(path, key, value):
key = key.replace(ns, '')

return JuniperElement(key, value)

NodePort = namedtuple('HostPort', 'nodeName nodePort')
def parse_node_port(nodeport, delimiter=':'):
"""
Parse a string in format 'hostname' or 'hostname:port' and return them
as a 2-tuple.
"""
node, _, port = nodeport.partition(delimiter)
if port.isdigit():
port = int(port)
else:
port = None

return NodePort(str(node), port)

0 comments on commit b1daa08

Please sign in to comment.