From 28d5eebe7e1c3db933dae06a800f1e73f5bba155 Mon Sep 17 00:00:00 2001 From: Paul Querna Date: Mon, 23 Sep 2013 23:29:55 -0700 Subject: [PATCH] Use twisted application framework --- .gitignore | 2 + Makefile | 2 +- teeth_agent/client.py | 39 ++++++++++++++++--- teeth_agent/logging.py | 3 -- teeth_agent/protocol.py | 3 +- .../{scripts/teeth-agent.py => service.py} | 29 +++++--------- twisted/plugins/teeth_agent.py | 29 ++++++++++++++ 7 files changed, 76 insertions(+), 31 deletions(-) rename teeth_agent/{scripts/teeth-agent.py => service.py} (70%) mode change 100755 => 100644 create mode 100644 twisted/plugins/teeth_agent.py diff --git a/.gitignore b/.gitignore index 0a21c2f96..aefdc6b4c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ build/* _trial_temp/ subunit-output.txt test-report.xml +twisted/plugins/dropin.cache +twistd.log diff --git a/Makefile b/Makefile index d1e3913df..eefb23055 100644 --- a/Makefile +++ b/Makefile @@ -26,5 +26,5 @@ clean: find . -name '.coverage' -delete find . -name '_trial_coverage' -print0 | xargs --null rm -rf find . -name '_trial_temp' -print0 | xargs --null rm -rf - rm -rf dist build *.egg-info + rm -rf dist build *.egg-info twisted/plugins/dropin.cache diff --git a/teeth_agent/client.py b/teeth_agent/client.py index 69eff5d21..3f6b8e395 100644 --- a/teeth_agent/client.py +++ b/teeth_agent/client.py @@ -16,11 +16,15 @@ limitations under the License. import time import simplejson as json -from teeth_agent.protocol import TeethAgentProtocol + +from twisted.application.service import MultiService +from twisted.application.internet import TCPClient from twisted.internet.protocol import ReconnectingClientFactory -from twisted.python.failure import Failure -from twisted.internet import reactor from twisted.internet.defer import maybeDeferred +from twisted.internet.defer import DeferredList +from twisted.python.failure import Failure + +from teeth_agent.protocol import TeethAgentProtocol from teeth_agent.logging import get_logger log = get_logger() @@ -59,7 +63,7 @@ class TeethClientFactory(ReconnectingClientFactory, object): super(TeethClientFactory, self).clientConnectionLost(connector, reason) -class TeethClient(object): +class TeethClient(MultiService, object): """ High level Teeth Client. """ @@ -68,18 +72,35 @@ class TeethClient(object): def __init__(self, addrs): super(TeethClient, self).__init__() + self.setName('teeth-agent') self._client_encoder = self.client_encoder_cls() self._client_factory = self.client_factory_cls(self._client_encoder, self) self._start_time = time.time() self._clients = [] self._outmsg = [] self._connectaddrs = addrs + self._running = False self._handlers = { 'v1': { 'status': self._handle_status, } } + def startService(self): + """Start the Service.""" + super(TeethClient, self).startService() + self._running = True + self.start() + + def stopService(self): + """Stop the Service.""" + super(TeethClient, self).stopService() + self._running = False + dl = [] + for client in self._clients: + dl.append(client.loseConnectionSoon(timeout=0.05)) + return DeferredList(dl) + def remove_endpoint(self, host, port): """Remove an Agent Endpoint from the active list.""" @@ -101,9 +122,15 @@ class TeethClient(object): self._clients.append(client) def start(self): - """Start the agent.""" + """Start the agent, if running.""" + + if not self._running: + return + for host, port in self._connectaddrs: - reactor.connectTCP(host, port, self._client_factory) + service = TCPClient(host, port, self._client_factory) + service.setName("teeth-agent[%s:%d]".format(host, port)) + self.addService(service) self._connectaddrs = [] def _on_command(self, topic, message): diff --git a/teeth_agent/logging.py b/teeth_agent/logging.py index 195f5612a..610bac881 100644 --- a/teeth_agent/logging.py +++ b/teeth_agent/logging.py @@ -14,9 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. """ -import sys import structlog -import twisted CONFIGURED_LOGGING = False @@ -32,7 +30,6 @@ def configure(): CONFIGURED_LOGGING = True - twisted.python.log.startLogging(sys.stderr) structlog.configure( context_class=dict, cache_logger_on_first_use=True) diff --git a/teeth_agent/protocol.py b/teeth_agent/protocol.py index 8fcc64498..51c3e28bd 100644 --- a/teeth_agent/protocol.py +++ b/teeth_agent/protocol.py @@ -18,6 +18,7 @@ import simplejson as json import uuid from twisted.internet import defer +from twisted.internet import task from twisted.internet import reactor from twisted.protocols.basic import LineReceiver from twisted.python.failure import Failure @@ -100,7 +101,7 @@ class RPCProtocol(LineReceiver, EventEmitter): """Attempt to disconnect from the transport as 'nicely' as possible. """ self._log.info('Trying to disconnect.') self.transport.loseConnection() - reactor.callLater(timeout, self.transport.abortConnection) + return task.deferLater(reactor, timeout, self.transport.abortConnection) def connectionMade(self): """TCP hard. We made it. Maybe.""" diff --git a/teeth_agent/scripts/teeth-agent.py b/teeth_agent/service.py old mode 100755 new mode 100644 similarity index 70% rename from teeth_agent/scripts/teeth-agent.py rename to teeth_agent/service.py index c765f29f7..dcfd88594 --- a/teeth_agent/scripts/teeth-agent.py +++ b/teeth_agent/service.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ Copyright 2013 Rackspace, Inc. @@ -17,17 +16,15 @@ limitations under the License. import os import sys -from os.path import dirname - -sys.path.append(dirname(dirname(dirname(os.path.realpath(__file__))))) - from twisted.python import usage -from twisted.internet import reactor +from twisted.application.service import MultiService + from teeth_agent.logging import configure as configureLogging from teeth_agent.agent import StandbyAgent class Options(usage.Options): + """Additional options for the Teeth Agent""" synopsis = """%s [options] """ % ( os.path.basename(sys.argv[0]),) @@ -35,24 +32,16 @@ class Options(usage.Options): optParameters = [["mode", "m", "standbye", "Mode to run Agent in, standbye or decom."]] -def run(): - if len(sys.argv) == 1: - sys.argv.append("--help") - - config = Options() - try: - config.parseOptions() - except usage.error, ue: - raise SystemExit("%s: %s" % (sys.argv[0], ue)) - +def makeService(config): + """Create an instance of the Teeth-Agent service.""" configureLogging() + s = MultiService() + if config['mode'] == "standbye": agent = StandbyAgent([['localhost', 8081]]) - agent.start() + agent.setServiceParent(s) else: raise SystemExit("Invalid mode") - reactor.run() -if __name__ == "__main__": - run() + return s diff --git a/twisted/plugins/teeth_agent.py b/twisted/plugins/teeth_agent.py new file mode 100644 index 000000000..66759f317 --- /dev/null +++ b/twisted/plugins/teeth_agent.py @@ -0,0 +1,29 @@ +""" +Copyright 2013 Rackspace, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + + +""" +Teeth Agent Twisted Application Plugin. +""" + +from twisted.application.service import ServiceMaker + +TeethAgent = ServiceMaker( + "Teeth Agent Client Application", + "teeth_agent.service", + "Teeth Agent for decomissioning and standbye", + "teeth-agent" +) \ No newline at end of file