Advertisement
Guest User

Untitled

a guest
Apr 19th, 2016
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.29 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2.  
  3. """
  4. Login and basic command-line interaction support using the Twisted asynchronous
  5. I/O framework. The Trigger Twister is just like the Mersenne Twister, except
  6. not at all.
  7. """
  8.  
  9. import copy
  10. import fcntl
  11. import os
  12. import re
  13. import signal
  14. import socket
  15. import struct
  16. import sys
  17. import tty
  18. from twisted.conch.client.default import SSHUserAuthClient
  19. from twisted.conch.ssh import channel, common, session, transport
  20. from twisted.conch.ssh.common import NS
  21. from twisted.conch.ssh.channel import SSHChannel
  22. from twisted.conch.endpoints import SSHCommandClientEndpoint, _NewConnectionHelper, _CommandTransport, TCP4ClientEndpoint, connectProtocol
  23. from twisted.conch.ssh.connection import SSHConnection
  24. from twisted.conch import telnet
  25. from twisted.internet import defer, protocol, reactor, stdio
  26. from twisted.protocols.policies import TimeoutMixin
  27. from twisted.python import log
  28. from twisted.python.usage import Options
  29. from xml.etree.ElementTree import (Element, ElementTree, XMLTreeBuilder)
  30.  
  31. from trigger.conf import settings
  32. from trigger import tacacsrc, exceptions
  33. # from trigger.twister import *
  34. from trigger.utils import network, cli
  35.  
  36.  
  37. class SSHSessionAddress(object):
  38. def __init__(self, server, username, command):
  39. self.server = server
  40. self.username = username
  41. self.command = command
  42.  
  43.  
  44. class _TriggerShellChannel(SSHChannel):
  45. name = b'session'
  46.  
  47. def __init__(self, creator, protocolFactory, commandConnected, commands,
  48. incremental, with_errors, timeout, command_interval):
  49. SSHChannel.__init__(self)
  50. self._creator = creator
  51. self._protocolFactory = protocolFactory
  52. self._commandConnected = commandConnected
  53. self.commands = commands
  54. self.incremental = incremental
  55. self.with_errors = with_errors
  56. self.timeout = timeout
  57. self.command_interval = command_interval
  58. self._reason = None
  59.  
  60. def openFailed(self, reason):
  61. """
  62. """
  63. self._commandConnected.errback(reason)
  64.  
  65.  
  66. def channelOpen(self, ignored):
  67. """
  68. """
  69. pr = session.packRequest_pty_req(os.environ['TERM'],
  70. self._get_window_size(), '')
  71.  
  72. self.conn.sendRequest(self, 'pty-req', pr)
  73.  
  74. command = self.conn.sendRequest(
  75. self, 'shell', '', wantReply=True)
  76. signal.signal(signal.SIGWINCH, self._window_resized)
  77. command.addCallbacks(self._execSuccess, self._execFailure)
  78.  
  79. def _window_resized(self, *args):
  80. """Triggered when the terminal is rezied."""
  81. win_size = self._get_window_size()
  82. new_size = win_size[1], win_size[0], win_size[2], win_size[3]
  83. self.conn.sendRequest(self, 'window-change',
  84. struct.pack('!4L', *new_size))
  85.  
  86. def _get_window_size(self):
  87. """Measure the terminal."""
  88. stdin_fileno = sys.stdin.fileno()
  89. winsz = fcntl.ioctl(stdin_fileno, tty.TIOCGWINSZ, '12345678')
  90. return struct.unpack('4H', winsz)
  91.  
  92. def _execFailure(self, reason):
  93. """
  94. """
  95. self._commandConnected.errback(reason)
  96.  
  97.  
  98. def _execSuccess(self, ignored):
  99. """
  100. """
  101. self._protocol = self._protocolFactory.buildProtocol(
  102. SSHSessionAddress(
  103. self.conn.transport.transport.getPeer(),
  104. self.conn.transport.creator.username,
  105. ''))
  106. self._bind_protocol_data()
  107. self._protocol.makeConnection(self)
  108. self._commandConnected.callback(self._protocol)
  109.  
  110. def _bind_protocol_data(self):
  111. import ipdb
  112. ipdb.set_trace()
  113. self._protocol.commands = self.commands or None
  114. self._protocol.commanditer = iter(self.commands) or None
  115. self._protocol.incremental = self.incremental or None
  116. self._protocol.with_errors = self.with_errors or None
  117. self._protocol.timeout = self.timeout or None
  118. self._protocol.command_interval = self.command_interval or None
  119.  
  120. def dataReceived(self, data):
  121. self._protocol.dataReceived(data)
  122. SSHChannel.dataReceived(self, data)
  123.  
  124.  
  125. class _TriggerSessionTransport(_CommandTransport):
  126. def verifyHostKey(self, hostKey, fingerprint):
  127. hostname = self.creator.hostname
  128. ip = self.transport.getPeer().host
  129.  
  130. self._state = b'SECURING'
  131. # d = self.creator.knownHosts.verifyHostKey(
  132. # self.creator.ui, hostname, ip, Key.fromString(hostKey))
  133. # d.addErrback(self._saveHostKeyFailure)
  134. return defer.succeed(1)
  135.  
  136.  
  137.  
  138. class _NewTriggerConnectionHelperBase(_NewConnectionHelper):
  139. """
  140. Return object used for establishing an async session rather than executing a single command.
  141. """
  142. def __init__(self, reactor, hostname, port, username, keys, password,
  143. agentEndpoint, knownHosts, ui):
  144. self.reactor = reactor
  145. self.hostname = hostname
  146. self.port = port
  147. self.username = username
  148. self.keys = keys
  149. self.password = password
  150. self.agentEndpoint = agentEndpoint
  151. if knownHosts is None:
  152. knownHosts = self._knownHosts()
  153. self.knownHosts = knownHosts
  154. self.ui = ui
  155.  
  156. def secureConnection(self):
  157. protocol = _TriggerSessionTransport(self)
  158. ready = protocol.connectionReady
  159.  
  160. sshClient = TCP4ClientEndpoint(self.reactor, self.hostname, self.port)
  161.  
  162. d = connectProtocol(sshClient, protocol)
  163. d.addCallback(lambda ignored: ready)
  164. return d
  165.  
  166.  
  167. class TriggerSSHShellClientEndpointBase(SSHCommandClientEndpoint):
  168. """
  169. Base class for SSH endpoints.
  170.  
  171. Subclass me when you want to create a new ssh client.
  172. """
  173. @classmethod
  174. def newConnection(cls, reactor, username, hostname, port=None,
  175. keys=None, password=None, agentEndpoint=None, knownHosts=None, ui=None):
  176.  
  177. helper = _NewTriggerConnectionHelperBase(
  178. reactor, hostname, port, username, keys, password,
  179. agentEndpoint, knownHosts, ui)
  180. return cls(helper)
  181.  
  182.  
  183. def __init__(self, creator):
  184. self._creator = creator
  185.  
  186. def _executeCommand(self, connection, protocolFactory, commands,
  187. incremental=None, with_errors=None, timeout=None, command_interval=None):
  188. commandConnected = defer.Deferred()
  189. def disconnectOnFailure(passthrough):
  190. # Close the connection immediately in case of cancellation, since
  191. # that implies user wants it gone immediately (e.g. a timeout):
  192. immediate = passthrough.check(CancelledError)
  193. self._creator.cleanupConnection(connection, immediate)
  194. return passthrough
  195. commandConnected.addErrback(disconnectOnFailure)
  196.  
  197. channel = _TriggerShellChannel(
  198. self._creator, protocolFactory, commandConnected, commands,
  199. incremental, with_errors, timeout, command_interval)
  200. connection.openChannel(channel)
  201. self.connected = True
  202. return commandConnected
  203.  
  204. def connect(self, factory, commands, incremental=None,
  205. with_errors=None, timeout=None, command_interval=None):
  206. d = self._creator.secureConnection()
  207. d.addCallback(self._executeCommand, factory, commands,
  208. incremental, with_errors, timeout, command_interval)
  209. return d
  210.  
  211.  
  212. class IoslikeSendExpect(protocol.Protocol, TimeoutMixin):
  213. """
  214. Action for use with TriggerTelnet as a state machine.
  215.  
  216. Take a list of commands, and send them to the device until we run out or
  217. one errors. Wait for a prompt after each.
  218. """
  219. # def __init__(self, device, commands, incremental=None, with_errors=False,
  220. # timeout=None, command_interval=0):
  221. def __init__(self):
  222. # self.device = self.transport.hostname
  223. # self._commands = self.transport.commands
  224. # self.commanditer = iter(_commands)
  225. # self.incremental = self.transport.incremental
  226. # self.with_errors = self.transport.with_errors
  227. # self.timeout = self.transport.timeout
  228. # self.command_interval = self.transport.command_interval
  229. self.prompt = re.compile('R1>')
  230. # self.prompt = re.compile(settings.IOSLIKE_PROMPT_PAT)
  231. # log.msg('[%s] My initialize commands: %s' % (self.device,
  232. # 'some shit'))
  233. self.initialized = True
  234.  
  235.  
  236. def connectionMade(self):
  237. """Do this when we connect."""
  238. import ipdb
  239. ipdb.set_trace()
  240. self.finished = defer.Deferred()
  241. self.setTimeout(self.timeout)
  242. self.results = self.factory.results = []
  243. self.data = ''
  244. log.msg('[%s] connectionMade, data: %r' % (self.device, self.data))
  245. self._send_next()
  246.  
  247.  
  248. def connectionLost(self, reason):
  249. self.finished.callback(None)
  250.  
  251.  
  252. # Don't call _send_next, since we expect to see a prompt, which
  253. # will kick off initialization.
  254.  
  255. def dataReceived(self, bytes):
  256. """Do this when we get data."""
  257. log.msg('[%s] BYTES: %r' % (self.device, bytes))
  258. self.data += bytes
  259.  
  260. # See if the prompt matches, and if it doesn't, see if it is waiting
  261. # for more input (like a [y/n]) prompt), and continue, otherwise return
  262. # None
  263. m = self.prompt.search(self.data)
  264. if not m:
  265. # If the prompt confirms set the index to the matched bytes,
  266. if is_awaiting_confirmation(self.data):
  267. log.msg('[%s] Got confirmation prompt: %r' % (self.device,
  268. self.data))
  269. prompt_idx = self.data.find(bytes)
  270. else:
  271. return None
  272. else:
  273. # Or just use the matched regex object...
  274. prompt_idx = m.start()
  275.  
  276. result = self.data[:prompt_idx]
  277. # Trim off the echoed-back command. This should *not* be necessary
  278. # since the telnet session is in WONT ECHO. This is confirmed with
  279. # a packet trace, and running self.transport.dont(ECHO) from
  280. # connectionMade() returns an AlreadyDisabled error. What's up?
  281. log.msg('[%s] result BEFORE: %r' % (self.device, result))
  282. result = result[result.find('\n')+1:]
  283. log.msg('[%s] result AFTER: %r' % (self.device, result))
  284.  
  285. if self.initialized:
  286. self.results.append(result)
  287.  
  288. if has_ioslike_error(result) and not self.with_errors:
  289. log.msg('[%s] Command failed: %r' % (self.device, result))
  290. self.factory.err = exceptions.IoslikeCommandFailure(result)
  291. self.transport.loseConnection()
  292. else:
  293. if self.command_interval:
  294. log.msg('[%s] Waiting %s seconds before sending next command' %
  295. (self.device, self.command_interval))
  296. reactor.callLater(self.command_interval, self._send_next)
  297.  
  298. def _send_next(self):
  299. """Send the next command in the stack."""
  300. self.data = ''
  301. self.resetTimeout()
  302.  
  303. if not self.initialized:
  304. log.msg('[%s] Not initialized, sending startup commands' %
  305. self.device)
  306. if self.startup_commands:
  307. next_init = self.startup_commands.pop(0)
  308. log.msg('[%s] Sending initialize command: %r' % (self.device,
  309. next_init))
  310. self.transport.write(next_init.strip() + self.device.delimiter)
  311. return None
  312. else:
  313. log.msg('[%s] Successfully initialized for command execution' %
  314. self.device)
  315. self.initialized = True
  316.  
  317. if self.incremental:
  318. self.incremental(self.results)
  319.  
  320. try:
  321. next_command = self.commanditer.next()
  322. except StopIteration:
  323. log.msg('[%s] No more commands to send, disconnecting...' %
  324. self.device)
  325. self.transport.loseConnection()
  326. return None
  327.  
  328. if next_command is None:
  329. self.results.append(None)
  330. self._send_next()
  331. else:
  332. log.msg('[%s] Sending command %r' % (self.device, next_command))
  333. self.transport.write(next_command + '\n')
  334.  
  335. def timeoutConnection(self):
  336. """Do this when we timeout."""
  337. log.msg('[%s] Timed out while sending commands' % self.device)
  338. self.factory.err = exceptions.CommandTimeout('Timed out while '
  339. 'sending commands')
  340. self.transport.loseConnection()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement