Advertisement
Guest User

Untitled

a guest
Oct 19th, 2011
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.34 KB | None | 0 0
  1. root@noc management/commands# cat debug-script.py
  2. # -*- coding: utf-8 -*-
  3. ##----------------------------------------------------------------------
  4. ## Usage: debug-script <profile> <script> <stream-url>
  5. ##
  6. ## WARNING!!!
  7. ## This module implements part of activator functionality.
  8. ## Sometimes via dirty hacks
  9. ##----------------------------------------------------------------------
  10. ## Copyright (C) 2007-2011 The NOC Project
  11. ## See LICENSE for details
  12. ##----------------------------------------------------------------------
  13. """
  14. """
  15. ## Python modules
  16. from __future__ import with_statement
  17. import logging
  18. import sys
  19. import ConfigParser
  20. import Queue
  21. import time
  22. import cPickle
  23. import threading
  24. import signal
  25. import os
  26. import datetime
  27. import pprint
  28. from optparse import OptionParser, make_option
  29. ## Django modules
  30. from django.core.management.base import BaseCommand, CommandError
  31. ## NOC modules
  32. from noc.sa.profiles import profile_registry
  33. from noc.sa.script import script_registry, Script
  34. from noc.sa.script.ssh.keys import Key
  35. from noc.sa.activator import Service, ServersHub
  36. from noc.sa.protocols.sae_pb2 import *
  37. from noc.sa.rpc import TransactionFactory
  38. from noc.lib.url import URL
  39. from noc.lib.nbsocket import SocketFactory, UDPSocket
  40. from noc.lib.validators import is_int
  41. from noc.lib.fileutils import read_file
  42.  
  43. class Controller(object):
  44. pass
  45.  
  46. ##
  47. ## Canned beef output
  48. ##
  49. class SessionCan(object):
  50. def __init__(self, script_name, input={}):
  51. self.cli = {} # Command -> result
  52. self.input = input
  53. self.result = None
  54. self.motd = ""
  55. self.script_name = script_name
  56. self.snmp_get = {}
  57. self.snmp_getnext = {}
  58. self.platform = "<<<INSERT YOUR PLATFORM HERE>>>"
  59. self.version = "<<<INSERT YOUR VERSION HERE>>>"
  60.  
  61. def set_version(self, platform, version):
  62. self.platform = platform
  63. self.version = version
  64.  
  65. ## Store data
  66. def save_interaction(self, provider, cmd, data):
  67. if provider == "cli":
  68. self.cli[cmd] = data
  69.  
  70. ##
  71. def save_snmp_get(self, oid, result):
  72. self.snmp_get[oid] = result
  73.  
  74. ##
  75. def save_snmp_getnext(self, oid, result):
  76. self.snmp_getnext[oid] = result
  77.  
  78. ## Save final result
  79. def save_result(self, result, motd=""):
  80. self.result = result
  81. self.motd = motd
  82.  
  83. ## Dump canned data
  84. def dump(self, output):
  85. def format_stringdict(d):
  86. def lrepr(s):
  87. return repr(s)[1:-1]
  88.  
  89. out = ["{"]
  90. for k, v in d.items():
  91. lines = v.splitlines()
  92. if len(lines) < 4:
  93. out += ["%s: %s, " % (repr(k), repr(v))]
  94. else:
  95. out += ["## %s" % repr(k)]
  96. out += ["%s: \"\"\"%s" % (repr(k), lrepr(lines[0]))]
  97. out += [lrepr(l) for l in lines[1:-1]]
  98. out += ["%s\"\"\", " % lrepr(lines[-1])]
  99. out += ["}"]
  100. return "\n".join(out)
  101.  
  102. vendor, profile, script = self.script_name.split(".")
  103. date = str(datetime.datetime.now()).split(".")[0]
  104. s = """# -*- coding: utf-8 -*-
  105. ##----------------------------------------------------------------------
  106. ## %(script)s test
  107. ## Auto-generated by ./noc debug-script at %(date)s
  108. ##----------------------------------------------------------------------
  109. ## Copyright (C) 2007-%(year)d The NOC Project
  110. ## See LICENSE for details
  111. ##----------------------------------------------------------------------
  112.  
  113. ## NOC modules
  114. from noc.lib.test import ScriptTestCase
  115.  
  116.  
  117. class %(test_name)s_Test(ScriptTestCase):
  118. script = "%(script)s"
  119. vendor = "%(vendor)s"
  120. platform = '%(platform)s'
  121. version = '%(version)s'
  122. input = %(input)s
  123. result = %(result)s
  124. motd = %(motd)s
  125. cli = %(cli)s
  126. snmp_get = %(snmp_get)s
  127. snmp_getnext = %(snmp_getnext)s
  128. """ % {
  129. "test_name" : self.script_name.replace(".", "_"),
  130. "script" : self.script_name,
  131. "vendor" : vendor,
  132. "year" : datetime.datetime.now().year,
  133. "date" : date,
  134. "input" : pprint.pformat(self.input),
  135. "result" : pprint.pformat(self.result),
  136. "cli" : format_stringdict(self.cli),
  137. "snmp_get" : pprint.pformat(self.snmp_get),
  138. "snmp_getnext" : pprint.pformat(self.snmp_getnext),
  139. "motd" : pprint.pformat(self.motd),
  140. "platform" : self.platform,
  141. "version" : self.version
  142. }
  143. with open(output, "w") as f:
  144. f.write(s)
  145.  
  146. class ActivatorStubFactory(SocketFactory):
  147. def unregister_socket(self, socket):
  148. super(ActivatorStubFactory, self).unregister_socket(socket)
  149. if isinstance(socket, UDPSocket):
  150. # Reset activator watchdog on UDP socket closing
  151. self.controller.reset_wait_ticks()
  152.  
  153. ##
  154. ## Activator emulation
  155. ##
  156. class ActivatorStub(object):
  157. WAIT_TICKS = 4
  158. def __init__(self, script_name, values=[], output=None):
  159. # Simple config stub
  160. self.config = ConfigParser.SafeConfigParser()
  161. self.config.read("etc/noc-activator.defaults")
  162. self.config.read("etc/noc-activator.conf")
  163. self.script_call_queue = Queue.Queue()
  164. self.ping_check_results = None
  165. self.factory = SocketFactory(tick_callback=self.tick,
  166. controller=self)
  167. self.servers = ServersHub(self)
  168. self.log_cli_sessions = None
  169. self.wait_ticks = self.WAIT_TICKS
  170. self.to_save_output = output is not None
  171. self.output = output
  172. self.use_canned_session = False
  173. self.scripts = []
  174. if self.to_save_output:
  175. self.script_name = script_name
  176. args = {}
  177. for k, v in values:
  178. args[k] = cPickle.loads(v)
  179. self.session_can = SessionCan(self.script_name, args)
  180. # SSH keys
  181. self.ssh_public_key = None
  182. self.ssh_private_key = None
  183. self.load_ssh_keys()
  184.  
  185. ##
  186. ## Initialize ssh keys
  187. ##
  188. def load_ssh_keys(self):
  189. private_path = self.config.get("ssh", "key")
  190. public_path = private_path + ".pub"
  191. # Load keys
  192. logging.debug("Loading private ssh key from '%s'" % private_path)
  193. s_priv = read_file(private_path)
  194. logging.debug("Loading public ssh key from '%s'" % public_path)
  195. s_pub = read_file(public_path)
  196. # Check all keys presend
  197. if s_priv is None or s_pub is None:
  198. self.error("Cannot find ssh keys. Generate one by './noc generate-ssh-keys' command")
  199. os._exit(1)
  200. self.ssh_public_key = Key.from_string(s_pub)
  201. self.ssh_private_key = Key.from_string_private_noc(s_priv)
  202.  
  203. def reset_wait_ticks(self):
  204. logging.debug("Resetting wait ticks")
  205. self.wait_ticks = self.WAIT_TICKS
  206.  
  207. def tick(self):
  208. logging.debug("Tick")
  209. while not self.script_call_queue.empty():
  210. try:
  211. f, args, kwargs = self.script_call_queue.get_nowait()
  212. except:
  213. break
  214. logging.debug("Calling delayed %s(*%s, **%s)" % (f, args, kwargs))
  215. apply(f, args, kwargs)
  216. if len(self.factory.sockets) == 0:
  217. self.wait_ticks -= 1
  218. if self.wait_ticks == 0:
  219. logging.debug("EXIT")
  220. if self.to_save_output:
  221. if self.session_can.result:
  222. logging.debug("Writing session test to %s" % self.output)
  223. self.session_can.dump(self.output)
  224. else:
  225. logging.error("Cannot write session test due to errors")
  226. # Finally dump results
  227. for s in self.scripts:
  228. if s.result:
  229. # Format output
  230. r = cPickle.loads(s.result)
  231. if isinstance(r, basestring):
  232. r = unicode(str(r), "utf8")
  233. else:
  234. r = pprint.pformat(r)
  235. logging.debug(u"SCRIPT RESULT: %s\n%s" % (s.debug_name, r))
  236. self.factory.shutdown()
  237. logging.debug("%d TICKS TO EXIT" % self.wait_ticks)
  238. else:
  239. # Sockets left
  240. self.reset_wait_ticks()
  241.  
  242.  
  243. def on_script_exit(self, script):
  244. get_version = ".".join(script.name.split(".")[:-1]) + ".get_version"
  245. if self.to_save_output and get_version in script.call_cache:
  246. v = script.call_cache[get_version]["{}"]
  247. self.session_can.set_version(v["platform"], v["version"])
  248. if script.parent is None:
  249. self.servers.close()
  250.  
  251. def run_script(self, _script_name, access_profile, callback, timeout=0, **kwargs):
  252. pv, pos, sn = _script_name.split(".", 2)
  253. profile = profile_registry["%s.%s" % (pv, pos)]()
  254. script = script_registry[_script_name](profile, self, access_profile, **kwargs)
  255. self.scripts += [script]
  256. if self.to_save_output:
  257. platform = None
  258. version = None
  259. for a in access_profile.attrs:
  260. if a.key == "platform":
  261. platform = a.value
  262. elif a.key == "version":
  263. version = a.value
  264. if platform and version:
  265. self.session_can.set_version(platform, version)
  266. script.start()
  267.  
  268. def request_call(self, f, *args, **kwargs):
  269. logging.debug("Requesting call: %s(*%s, **%s)" % (f, args, kwargs))
  270. self.script_call_queue.put((f, args, kwargs))
  271.  
  272. def can_run_script(self):
  273. return True
  274. ##
  275. ## Handler to accept canned input
  276. ##
  277. def save_interaction(self, provider, cmd, data):
  278. self.session_can.save_interaction(provider, cmd, data)
  279. ##
  280. ## Handler to save final result
  281. ##
  282. def save_result(self, result, motd=""):
  283. self.session_can.save_result(result, motd)
  284. ##
  285. def save_snmp_get(self, oid, result):
  286. self.session_can.save_snmp_get(oid, result)
  287. ##
  288. def save_snmp_getnext(self, oid, result):
  289. self.session_can.save_snmp_getnext(oid, result)
  290. ##
  291. def error(self, msg):
  292. logging.error(msg)
  293.  
  294. ##
  295. ## debug-script handler
  296. ##
  297. class Command(BaseCommand):
  298. help = "Debug SA Script"
  299. option_list = BaseCommand.option_list + (
  300. make_option("-c", "--read-community", dest="snmp_ro"),
  301. make_option("-o", "--output", dest="output"),
  302. make_option("-p", "--profile", dest="profile", action="store_true")
  303. )
  304.  
  305. ##
  306. ## Gentle SIGINT handler
  307. ##
  308. def SIGINT(self, signo, frame):
  309. logging.info("SIGINT")
  310. os._exit(0)
  311.  
  312. ##
  313. ## Print usage and exit
  314. ##
  315. def _usage(self):
  316. print "USAGE:"
  317. print "%s debug-script [-c <community>] [-o <output>] <script> <obj1> [ .. <objN>] [<key1>=<value1> [ .. <keyN>=<valueN>]]" % sys.argv[0]
  318. print "Where:"
  319. print "\t-c <community> - SNMP RO Community"
  320. print "\t-o <output> - Canned beef output"
  321. print "\t--profile - Run through python profiler"
  322. return
  323.  
  324. ##
  325. ## Create access profile from URL
  326. ##
  327. def set_access_profile_url(self, access_profile, obj, profile, snmp_ro_community):
  328. if profile is None:
  329. raise CommandError("Script name must contain profile when using URLs")
  330. url = URL(obj)
  331. access_profile.profile = profile
  332. access_profile.scheme = Script.get_scheme_id(url.scheme)
  333. access_profile.address = url.host
  334. if url.port:
  335. access_profile.port = url.port
  336. access_profile.user = url.user
  337. if "\x00" in url.password:
  338. # Check the password really the pair of password/enable password
  339. p, s = url.password.split("\x00", 1)
  340. access_profile.password = p
  341. access_profile.super_password = s
  342. else:
  343. access_profile.password = url.password
  344. access_profile.path = url.path
  345. if snmp_ro_community:
  346. access_profile.snmp_ro = snmp_ro_community
  347.  
  348. ##
  349. ## Create access profile from Database
  350. ##
  351. def set_access_profile_name(self, access_profile, obj, profile, snmp_ro_community):
  352. from noc.sa.models import ManagedObject
  353. from django.db.models import Q
  354.  
  355. # Prepare query
  356. if is_int(obj):
  357. q = Q(id=int(obj)) | Q(name=obj) # Integers can be object id or name
  358. else:
  359. q = Q(name=obj) # Search by name otherwise
  360. # Get object from database
  361. try:
  362. o = ManagedObject.objects.get(q)
  363. except ManagedObject.DoesNotExist:
  364. raise CommandError("Object not found: %s" % obj)
  365. # Fill access profile
  366. access_profile.profile = o.profile_name
  367. access_profile.scheme = o.scheme
  368. access_profile.address = o.address
  369. if o.port:
  370. access_profile.port = o.port
  371. access_profile.user = o.user
  372. access_profile.password = o.password
  373. if o.super_password:
  374. access_profile.super_password = o.super_password
  375. if snmp_ro_community:
  376. if snmp_ro_community != "-":
  377. access_profile.snmp_ro = snmp_ro_community
  378. elif o.snmp_ro:
  379. access_profile.snmp_ro = o.snmp_ro
  380. if o.remote_path:
  381. access_profile.path = o.remote_path
  382. attrs = [(a.key, a.value) for a in o.managedobjectattribute_set.all()]
  383. for k, v in attrs:
  384. a = access_profile.attrs.add()
  385. a.key = str(k)
  386. a.value = v
  387.  
  388. ##
  389. ## Prepare script request
  390. ##
  391. def get_request(self, script, obj, snmp_ro_community, values):
  392. vendor = None
  393. os_name = None
  394. profile = None
  395. r = ScriptRequest()
  396. # Normalize script name
  397. if "." in script:
  398. vendor, os_name, script = script.split(".", 2)
  399. profile = "%s.%s" % (vendor, os_name)
  400. # Fill access profile and script name
  401. if "://" in obj:
  402. # URL
  403. self.set_access_profile_url(r.access_profile, obj, profile, snmp_ro_community)
  404. r.script = "%s.%s" % (profile, script)
  405. else:
  406. # Database name or id
  407. self.set_access_profile_name(r.access_profile, obj, profile, snmp_ro_community)
  408. if profile and r.access_profile.profile != profile:
  409. raise CommandError("Profile mismatch for '%s'" % obj)
  410. r.script = "%s.%s" % (r.access_profile.profile, script)
  411. ## Fill values
  412. for k, v in values:
  413. a = r.kwargs.add()
  414. a.key = k
  415. a.value = v
  416. return r
  417.  
  418. ##
  419. ## Expand names starting with "selector:"
  420. ##
  421. def expand_selectors(self, objects):
  422. if [o for o in objects if o.startswith("selector:")]:
  423. # Has selectors
  424. from noc.sa.models import ManagedObjectSelector
  425.  
  426. r = set()
  427. for o in objects:
  428. if o.startswith("selector:"):
  429. o = o[9:]
  430. try:
  431. s = ManagedObjectSelector.objects.get(name=o)
  432. except ManagedObjectSelector.DoesNotExist:
  433. raise CommandError("Selector not found: %s" % o)
  434. r |= set([mo.name for mo in s.managed_objects])
  435. else:
  436. r.add(o)
  437. return list(r)
  438. else:
  439. # No selectors. Nothing to expand
  440. return objects
  441.  
  442. ##
  443. def run_script(self, service, request):
  444. def handle_callback(controller, response=None, error=None):
  445. if error:
  446. logging.debug("Error: %s" % error.text)
  447. if response:
  448. logging.debug("Script completed")
  449. logging.debug(response.config)
  450. logging.debug("Running script thread")
  451. controller = Controller()
  452. controller.transaction = self.tf.begin()
  453. service.script(controller=controller, request=request, done=handle_callback)
  454.  
  455. ##
  456. ## Handle command
  457. ##
  458. def handle(self, *args, **options):
  459. if len(args) < 2:
  460. return self._usage()
  461. script_name = args[0]
  462. objects = []
  463. values = []
  464. # Parse args
  465. for a in args[1:]:
  466. if "=" in a:
  467. # key=value
  468. k, v = a.split("=", 1)
  469. v = cPickle.dumps(eval(v, {}, {}))
  470. values += [(k, v)]
  471. else:
  472. # object
  473. objects += [a]
  474. # Canned beef for only one object
  475. output = options.get("output", None)
  476. if output and len(objects) != 1:
  477. raise CommandError("Can write canned beef for one object only")
  478. # Get SNMP community
  479. snmp_ro_community = None
  480. if options["snmp_ro"]:
  481. snmp_ro_community = options["snmp_ro"]
  482. # Prepare requests
  483. objects = self.expand_selectors(objects)
  484. requests = [self.get_request(script_name, obj, snmp_ro_community, values) for obj in objects]
  485. # Set up logging and signal handlers
  486. logging.root.setLevel(logging.DEBUG)
  487. signal.signal(signal.SIGINT, self.SIGINT)
  488. ## Prepare activator stub
  489. self.tf = TransactionFactory()
  490. service = Service()
  491. service.activator = ActivatorStub(requests[0].script if output else None, values, output)
  492.  
  493. ## Run scripts
  494. def run():
  495. for r in requests:
  496. print r
  497. t = threading.Thread(target=self.run_script, args=(service, r))
  498. t.start()
  499. # Finally give control to activator's factory
  500. service.activator.factory.run(run_forever=True)
  501.  
  502. if options.get("profile", True):
  503. logging.debug("Enabling python profiler")
  504. import cProfile
  505. cProfile.runctx("run()", globals(), locals())
  506. else:
  507. run()
  508.  
  509.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement