Use twisted application framework

This commit is contained in:
Paul Querna 2013-09-23 23:29:55 -07:00
parent 21e7a794bd
commit 28d5eebe7e
7 changed files with 76 additions and 31 deletions

2
.gitignore vendored
View File

@ -9,3 +9,5 @@ build/*
_trial_temp/ _trial_temp/
subunit-output.txt subunit-output.txt
test-report.xml test-report.xml
twisted/plugins/dropin.cache
twistd.log

View File

@ -26,5 +26,5 @@ clean:
find . -name '.coverage' -delete find . -name '.coverage' -delete
find . -name '_trial_coverage' -print0 | xargs --null rm -rf find . -name '_trial_coverage' -print0 | xargs --null rm -rf
find . -name '_trial_temp' -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

View File

@ -16,11 +16,15 @@ limitations under the License.
import time import time
import simplejson as json 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.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 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 from teeth_agent.logging import get_logger
log = get_logger() log = get_logger()
@ -59,7 +63,7 @@ class TeethClientFactory(ReconnectingClientFactory, object):
super(TeethClientFactory, self).clientConnectionLost(connector, reason) super(TeethClientFactory, self).clientConnectionLost(connector, reason)
class TeethClient(object): class TeethClient(MultiService, object):
""" """
High level Teeth Client. High level Teeth Client.
""" """
@ -68,18 +72,35 @@ class TeethClient(object):
def __init__(self, addrs): def __init__(self, addrs):
super(TeethClient, self).__init__() super(TeethClient, self).__init__()
self.setName('teeth-agent')
self._client_encoder = self.client_encoder_cls() self._client_encoder = self.client_encoder_cls()
self._client_factory = self.client_factory_cls(self._client_encoder, self) self._client_factory = self.client_factory_cls(self._client_encoder, self)
self._start_time = time.time() self._start_time = time.time()
self._clients = [] self._clients = []
self._outmsg = [] self._outmsg = []
self._connectaddrs = addrs self._connectaddrs = addrs
self._running = False
self._handlers = { self._handlers = {
'v1': { 'v1': {
'status': self._handle_status, '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): def remove_endpoint(self, host, port):
"""Remove an Agent Endpoint from the active list.""" """Remove an Agent Endpoint from the active list."""
@ -101,9 +122,15 @@ class TeethClient(object):
self._clients.append(client) self._clients.append(client)
def start(self): def start(self):
"""Start the agent.""" """Start the agent, if running."""
if not self._running:
return
for host, port in self._connectaddrs: 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 = [] self._connectaddrs = []
def _on_command(self, topic, message): def _on_command(self, topic, message):

View File

@ -14,9 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
""" """
import sys
import structlog import structlog
import twisted
CONFIGURED_LOGGING = False CONFIGURED_LOGGING = False
@ -32,7 +30,6 @@ def configure():
CONFIGURED_LOGGING = True CONFIGURED_LOGGING = True
twisted.python.log.startLogging(sys.stderr)
structlog.configure( structlog.configure(
context_class=dict, context_class=dict,
cache_logger_on_first_use=True) cache_logger_on_first_use=True)

View File

@ -18,6 +18,7 @@ import simplejson as json
import uuid import uuid
from twisted.internet import defer from twisted.internet import defer
from twisted.internet import task
from twisted.internet import reactor from twisted.internet import reactor
from twisted.protocols.basic import LineReceiver from twisted.protocols.basic import LineReceiver
from twisted.python.failure import Failure from twisted.python.failure import Failure
@ -100,7 +101,7 @@ class RPCProtocol(LineReceiver, EventEmitter):
"""Attempt to disconnect from the transport as 'nicely' as possible. """ """Attempt to disconnect from the transport as 'nicely' as possible. """
self._log.info('Trying to disconnect.') self._log.info('Trying to disconnect.')
self.transport.loseConnection() self.transport.loseConnection()
reactor.callLater(timeout, self.transport.abortConnection) return task.deferLater(reactor, timeout, self.transport.abortConnection)
def connectionMade(self): def connectionMade(self):
"""TCP hard. We made it. Maybe.""" """TCP hard. We made it. Maybe."""

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
""" """
Copyright 2013 Rackspace, Inc. Copyright 2013 Rackspace, Inc.
@ -17,17 +16,15 @@ limitations under the License.
import os import os
import sys import sys
from os.path import dirname
sys.path.append(dirname(dirname(dirname(os.path.realpath(__file__)))))
from twisted.python import usage 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.logging import configure as configureLogging
from teeth_agent.agent import StandbyAgent from teeth_agent.agent import StandbyAgent
class Options(usage.Options): class Options(usage.Options):
"""Additional options for the Teeth Agent"""
synopsis = """%s [options] synopsis = """%s [options]
""" % ( """ % (
os.path.basename(sys.argv[0]),) 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."]] optParameters = [["mode", "m", "standbye", "Mode to run Agent in, standbye or decom."]]
def run(): def makeService(config):
if len(sys.argv) == 1: """Create an instance of the Teeth-Agent service."""
sys.argv.append("--help")
config = Options()
try:
config.parseOptions()
except usage.error, ue:
raise SystemExit("%s: %s" % (sys.argv[0], ue))
configureLogging() configureLogging()
s = MultiService()
if config['mode'] == "standbye": if config['mode'] == "standbye":
agent = StandbyAgent([['localhost', 8081]]) agent = StandbyAgent([['localhost', 8081]])
agent.start() agent.setServiceParent(s)
else: else:
raise SystemExit("Invalid mode") raise SystemExit("Invalid mode")
reactor.run()
if __name__ == "__main__": return s
run()

View File

@ -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"
)