PK#vHS^#++ezoutlet/constants.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from . import __version__ from . import __name__ as package_name import os PROGRAM_NAME = os.path.basename(__file__) VERSION_FORMAT_STRING = package_name + ' {0}' VERSION_STRING = VERSION_FORMAT_STRING.format(__version__) DEFAULT_EZ_OUTLET_RESET_INTERVAL = 3.05 EXIT_CODE_OK = 0 EXIT_CODE_ERR = 1 EXIT_CODE_PARSER_ERR = 2 # Arguments and commands RESET_TIME_ARG_SHORT = '-t' RESET_TIME_ARG_LONG = '--reset-time' # Help strings HELP_TEXT = ( """Control an ezOutlet EZ-11b device.""" ) HELP_TEXT_RESET = "Send reset command; wait for on/off cycle." HELP_TEXT_VERSION = "Print version" HELP_TEXT_TARGET_ARG = 'IP address/hostname of ezOutlet device.' HELP_TEXT_RESET_TIME_ARG = 'Extra time in seconds to wait, e.g. for device reboot.' \ ' Note that the script already waits {0} seconds for the' \ ' ezOutlet to turn off and on.'.format(DEFAULT_EZ_OUTLET_RESET_INTERVAL) # Errors ERROR_STRING = "{0}: error: {1}" UNHANDLED_ERROR_MESSAGE = "Unhandled exception! Please file bug report.\n\n{0}" RESET_TIME_NEGATIVE_ERROR_MESSAGE = "argument{0}/{1}: value must be non-negative.".format(RESET_TIME_ARG_LONG, RESET_TIME_ARG_SHORT) PKkH2nezoutlet/error_handling.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals import traceback from . import parser, constants def usage_error(exception): """Handle an error in command line arguments. :param exception: Exception caught. :return: constants.EXIT_CODE_PARSER_ERR """ parser.print_usage() parser.print_error(msg=exception) return constants.EXIT_CODE_PARSER_ERR def runtime_error(exception): """Handle a runtime error, e.g., an unresponsive server. :param exception: Exception caught. :return: constants.EXIT_CODE_ERR """ parser.print_error(msg=exception) return constants.EXIT_CODE_ERR def unexpected_exception(exception): """Handle an unexpected exception. :param exception: Exception caught. :return: constants.EXIT_CODE_ERR """ _ = exception # exception gets printed by traceback.format_exc() parser.print_error(msg=constants.UNHANDLED_ERROR_MESSAGE.format(traceback.format_exc())) return constants.EXIT_CODE_ERR PKkH!ezoutlet/exceptions.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals class EzOutletError(Exception): pass class EzOutletUsageError(EzOutletError): pass PKkHjezoutlet/ez_outlet.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from future.utils import raise_ import sys import time try: # Python 2 import urlparse except ImportError: # Python 3 # noinspection PyUnresolvedReferences import urllib.parse as urlparse import requests from . import constants from . import exceptions def _get_url(hostname, path): return urlparse.urlunparse(('http', hostname, path, '', '', '')) class EzOutlet: """Uses ezOutlet EZ-11b to reset a device. Uses the ezOutlet EZ-11b Internet IP-Enabled Remote Power Reboot Switch to reset a device under test (DUT). In addition to reset(), post_fail() is provided, meant to be given as a callback to a Session object. It uses undocumented but simple CGI scripts. """ DEFAULT_EZ_OUTLET_RESET_INTERVAL = constants.DEFAULT_EZ_OUTLET_RESET_INTERVAL DEFAULT_TIMEOUT = 10 DEFAULT_WAIT_TIME = 0 RESET_URL_PATH = '/reset.cgi' EXPECTED_RESPONSE_CONTENTS = '0,0' NO_RESPONSE_MSG = "No response from EzOutlet after {0} seconds." UNEXPECTED_RESPONSE_MSG = ("Unexpected response from EzOutlet. Expected: " + repr(EXPECTED_RESPONSE_CONTENTS) + " Actual: {0}") LOG_REQUEST_MSG = 'HTTP GET {0}' def __init__(self, hostname, timeout=DEFAULT_TIMEOUT): """ Args: hostname: Hostname or IP address of device. timeout: Time in seconds to wait for the EzOutlet to respond. """ self._hostname = hostname self._timeout = timeout @property def url(self): return _get_url(self._hostname, self.RESET_URL_PATH) def reset(self, post_reset_delay=DEFAULT_WAIT_TIME, ez_outlet_reset_interval=DEFAULT_EZ_OUTLET_RESET_INTERVAL): """Send reset request to ezOutlet, check response, wait for reset. After sending HTTP request and receiving response, wait dut_reset_delay + ez_outlet_reset_interval seconds. If the outlet does not respond (after self._timeout seconds), or gives an unexpected response, this method will raise an exception. Args: post_reset_delay: Time in seconds to allow the device being reset to reboot. See also reset_delay. ez_outlet_reset_interval: Time to wait before returning (besides dut_reset_delay). This should be configured to match the time the ezOutlet device actually takes to turn off and on again. Set to 0 to make this method non-blocking. Returns: HTTP response contents. Raises: EzOutletResetError: If the reset fails due to: - no response in self._timeout seconds or - unexpected response contents (see EzOutletReset.EXPECTED_RESPONSE_CONTENTS) """ response = self._http_get(self.url) self._check_response_raise_if_unexpected(response) self._wait_for_reset(post_reset_delay + ez_outlet_reset_interval) return response def _http_get(self, url): """HTTP GET and return response. Args: url: Target to GET. Returns: Response contents. Raises: EzOutletResetError: If the reset fails due to: - no response in self._timeout seconds """ try: return requests.get(url, timeout=self._timeout, proxies={"http": None, "https": None}).text except requests.exceptions.ConnectTimeout: raise_(exceptions.EzOutletError( self.NO_RESPONSE_MSG.format(self._timeout)), None, sys.exc_info()[2]) def _check_response_raise_if_unexpected(self, response): """Raise if response is unexpected. Args: response: Response. Returns: None Raises: EzOutletResetError: If the reset fails due to: - unexpected response contents (see EzOutletReset.EXPECTED_RESPONSE_CONTENTS) """ if response != self.EXPECTED_RESPONSE_CONTENTS: raise exceptions.EzOutletError( self.UNEXPECTED_RESPONSE_MSG.format(response)) @staticmethod def _wait_for_reset(total_delay): """Sleep for self._reset_delay + self._dut_reset_time. Returns: None """ time.sleep(total_delay) PKkH]ezoutlet/icommand.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals import abc class ICommand(object): """ Interface for Commands for this application. Implementors: Parsed arguments from argparse should be taken and checked for validity in the constructor. """ @abc.abstractmethod def run(self): """ Run the command. Returns exit code. :rtype: int """ PKkHJG<<ezoutlet/no_command.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from .icommand import ICommand from . import parser from . import constants class NoCommand(ICommand): def __init__(self, parsed_args): self._args = parsed_args def run(self): parser.print_help() return constants.EXIT_CODE_PARSER_ERR PK#vH(dezoutlet/parser.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals import argparse import sys from . import constants def print_error(msg): print(constants.ERROR_STRING.format(constants.PROGRAM_NAME, msg), file=sys.stderr) def print_help(): print(static_parser.get_help(), file=sys.stderr) def print_usage(): print(static_parser.get_usage(), file=sys.stderr) class Parser(object): def __init__(self): self._parser = argparse.ArgumentParser(description=constants.HELP_TEXT) subparsers = self._parser.add_subparsers(dest='subcommand') _add_reset_parser(subparsers) _add_version_parser(subparsers) def get_usage(self): return self._parser.format_usage() def get_help(self): return self._parser.format_help() def parse_args(self, argv): return self._parser.parse_args(argv[1:]) def _add_reset_parser(subparsers): parser_reset = subparsers.add_parser('reset', help=constants.HELP_TEXT_RESET) parser_reset.add_argument('target', help=constants.HELP_TEXT_TARGET_ARG) parser_reset.add_argument(constants.RESET_TIME_ARG_LONG, constants.RESET_TIME_ARG_SHORT, type=float, default=0, help=constants.HELP_TEXT_RESET_TIME_ARG) def _add_version_parser(subparsers): subparsers.add_parser('version', help=constants.HELP_TEXT_VERSION) static_parser = Parser() PKkHodezoutlet/parse_command.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from .reset_command import ResetCommand from .no_command import NoCommand def parse_command(subcommand, parsed_args): if subcommand == 'reset': return ResetCommand(parsed_args=parsed_args) else: # Note: In Python 2, argparse will raise a SystemException when no # command is given, so this bit is for Python 3. return NoCommand(parsed_args=parsed_args) PKkH~a,:ffezoutlet/reset_command.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from . import exceptions from . import constants from . import ez_outlet from .icommand import ICommand class ResetCommand(ICommand): def __init__(self, parsed_args): self._args = parsed_args self._check_args() def _check_args(self): if self._args.reset_time < 0: raise exceptions.EzOutletUsageError(constants.RESET_TIME_NEGATIVE_ERROR_MESSAGE) def run(self): ez = ez_outlet.EzOutlet(hostname=self._args.target) ez.reset(post_reset_delay=self._args.reset_time) return constants.EXIT_CODE_OK PK#vHLX||ezoutlet/__init__.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals __version__ = '1.0' from . import constants from . import error_handling from . import exceptions from . import ez_outlet from . import parser from .commands import parse_command __all__ = [ez_outlet.EzOutlet, exceptions.EzOutletError, exceptions.EzOutletUsageError] def main(argv): try: return _parse_args_and_run(argv) except exceptions.EzOutletUsageError as e: return error_handling.usage_error(e) except exceptions.EzOutletError as e: return error_handling.runtime_error(e) except Exception as e: return error_handling.unexpected_exception(e) except SystemExit as e: return e.code def _parse_args_and_run(argv): parsed_args = parser.static_parser.parse_args(argv) cmd = parse_command.parse_command(parsed_args.subcommand, parsed_args) return cmd.run() PKkH>ezoutlet/commands/no_command.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from .icommand import ICommand from .. import parser from .. import constants class NoCommand(ICommand): def __init__(self, parsed_args): self._args = parsed_args def run(self): parser.print_help() return constants.EXIT_CODE_PARSER_ERR PK#vHTϪ??"ezoutlet/commands/parse_command.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from .no_command import NoCommand from .reset_command import ResetCommand from .version_command import VersionCommand def parse_command(subcommand, parsed_args): if subcommand == 'reset': return ResetCommand(parsed_args=parsed_args) elif subcommand == 'version': return VersionCommand(parsed_args=parsed_args) else: # Note: In Python 2, argparse will raise a SystemException when no # command is given, so this bit is for Python 3. return NoCommand(parsed_args=parsed_args) PK#vHii"ezoutlet/commands/reset_command.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from .. import exceptions from .. import constants from .. import ez_outlet from .icommand import ICommand class ResetCommand(ICommand): def __init__(self, parsed_args): self._args = parsed_args self._check_args() def _check_args(self): if self._args.reset_time < 0: raise exceptions.EzOutletUsageError(constants.RESET_TIME_NEGATIVE_ERROR_MESSAGE) def run(self): ez = ez_outlet.EzOutlet(hostname=self._args.target) ez.reset(post_reset_delay=self._args.reset_time) return constants.EXIT_CODE_OK PK#vH $ezoutlet/commands/version_command.py# Copyright (C) 2015 Schweitzer Engineering Laboratories, Inc. # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from .. import constants from .icommand import ICommand class VersionCommand(ICommand): def __init__(self, parsed_args): self._args = parsed_args self._check_args() def _check_args(self): # version command accepts anything pass def run(self): print(constants.VERSION_STRING) return constants.EXIT_CODE_OK PK#vHezoutlet/commands/__init__.pyPK:vH^- &ezoutlet-1.0.dist-info/DESCRIPTION.rstUNKNOWN PK:vH.F$ezoutlet-1.0.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Topic :: Security", "Topic :: Software Development :: Testing :: Traffic Generation"], "extensions": {"python.details": {"contacts": [{"email": "joshua.t.pereyda@gmail.com", "name": "Joshua Pereyda", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/jtpereyda/ezoutlet"}}}, "extras": ["dev"], "generator": "bdist_wheel (0.28.0)", "license": "MIT", "metadata_version": "2.0", "name": "ezoutlet", "run_requires": [{"requires": ["future", "requests"]}, {"extra": "dev", "requires": ["mock", "pytest"]}], "summary": "Command line tool and Python API for ezOutlet EZ-11b", "version": "1.0"}PK:vHDs $ezoutlet-1.0.dist-info/top_level.txtezoutlet PK:vH ttezoutlet-1.0.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.28.0) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any PK:vH7Faezoutlet-1.0.dist-info/METADATAMetadata-Version: 2.0 Name: ezoutlet Version: 1.0 Summary: Command line tool and Python API for ezOutlet EZ-11b Home-page: https://github.com/jtpereyda/ezoutlet Author: Joshua Pereyda Author-email: joshua.t.pereyda@gmail.com License: MIT Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Topic :: Security Classifier: Topic :: Software Development :: Testing :: Traffic Generation Requires-Dist: future Requires-Dist: requests Provides-Extra: dev Requires-Dist: mock; extra == 'dev' Requires-Dist: pytest; extra == 'dev' UNKNOWN PK:vH-$``ezoutlet-1.0.dist-info/RECORDezoutlet/__init__.py,sha256=lFVARqTtwQF5hpRRRbh8Z3FUg0bjsVctKw05-FbPOtw,1148 ezoutlet/__main__.py,sha256=UuRxiPKmlj-db7FldPPUd4onG85B-IB9r9fnmEfQzEs,94 ezoutlet/constants.py,sha256=k2cLudPrpJ2C72dJj4E7iR4OggDXR_a6V7QWJLiEId8,1579 ezoutlet/error_handling.py,sha256=PXiPs3Btm--tsDxGFB7dZJ3OLBBBXVawQFp7e0XbVnw,1245 ezoutlet/exceptions.py,sha256=vCkaLEp6WsfSpX9Wbqw3KjDNH6lo1gIilPumPhGcatQ,397 ezoutlet/ez_outlet.py,sha256=4P1r7Av6SrHATaQaxHmJaJs0D3SAz6cWJZNAwkbnUrA,4749 ezoutlet/icommand.py,sha256=5a6Zl6n8ZjPppftaCUoG9By1BI2YAPSTwBnuY1SIUDY,639 ezoutlet/no_command.py,sha256=UZqWb30JsglCVy-Tlv0JR-dLOLLXBIZOOnB47zqt9R4,572 ezoutlet/parse_command.py,sha256=DTCv2AbMWhBkoqFgh19AXvEm_BgBEXgsWK9mAj2OIiw,698 ezoutlet/parser.py,sha256=9Fg4yaR7wz36Dkk0UrtyOonk4oSMRPAexgY2bNNodKk,1687 ezoutlet/reset_command.py,sha256=Iyess05LTMF1dX0J1D0MOMwFEWiVEf5VfDCk-45XoiA,870 ezoutlet/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 ezoutlet/commands/icommand.py,sha256=5a6Zl6n8ZjPppftaCUoG9By1BI2YAPSTwBnuY1SIUDY,639 ezoutlet/commands/no_command.py,sha256=-csN7oYID-svFlyp4vJHActKhfGBvOmIGlZ6cjotsG0,574 ezoutlet/commands/parse_command.py,sha256=uHpbZvYzpJ8qEUsFXf6JsQ1FZZ1e_jd3D6YXuKAR-LY,831 ezoutlet/commands/reset_command.py,sha256=ufpzaxVI_AbK9ha3nJ1R4ikQ7h-Zk1LKUA3yGBMhswY,873 ezoutlet/commands/version_command.py,sha256=D3lN-NB1iL1T1aq4fXZWhJuxQELeBrCU0xFWxpfGmw8,672 ezoutlet-1.0.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 ezoutlet-1.0.dist-info/METADATA,sha256=Rn4nJlwIKkBi4wFbtvGsmyc2626Gl4cZyPxfDdjgzVg,944 ezoutlet-1.0.dist-info/RECORD,, ezoutlet-1.0.dist-info/WHEEL,sha256=KS6RdDYJ71bYvEj5fY27SEdZGSKwWLJ6D6uk1wqTCIE,116 ezoutlet-1.0.dist-info/metadata.json,sha256=mgI7JhS7R9iTXe9oed3WQXJCkw5pUgjdjtd3g7D1IHQ,994 ezoutlet-1.0.dist-info/top_level.txt,sha256=kgHFUl9Y8lvu6jjS-oL3f-hUtIa1LvMvQ-Eaxr-9JnY,9 PK#vHS^#++ezoutlet/constants.pyPKkH2n^ezoutlet/error_handling.pyPKkH!s ezoutlet/exceptions.pyPKkHj4 ezoutlet/ez_outlet.pyPKkH]ezoutlet/icommand.pyPKkHJG<<"ezoutlet/no_command.pyPK#vH(d%ezoutlet/parser.pyPKkHod+ezoutlet/parse_command.pyPKkH~a,:ff.ezoutlet/reset_command.pyPK#vHLX||j2ezoutlet/__init__.pyPKkH>b:ezoutlet/commands/no_command.pyPK#vHTϪ??"<ezoutlet/commands/parse_command.pyPK#vHii"\@ezoutlet/commands/reset_command.pyPK#vH $Dezoutlet/commands/version_command.pyPK#vHFezoutlet/commands/__init__.pyPK:vH^- &"Gezoutlet-1.0.dist-info/DESCRIPTION.rstPK:vH.F$pGezoutlet-1.0.dist-info/metadata.jsonPK:vHDs $Kezoutlet-1.0.dist-info/top_level.txtPK:vH ttKezoutlet-1.0.dist-info/WHEELPK:vH7FaLezoutlet-1.0.dist-info/METADATAPK:vH-$``zPezoutlet-1.0.dist-info/RECORDPKX