Merge in lbaas_mgm code and fix pep8 on it
This commit is contained in:
parent
c05150bfca
commit
a8e29e9fa8
0
lbaas_mgm/__init__.py
Normal file
0
lbaas_mgm/__init__.py
Normal file
62
lbaas_mgm/faults.py
Normal file
62
lbaas_mgm/faults.py
Normal file
@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import json
|
||||
|
||||
|
||||
class ServiceFault(object):
|
||||
def __init__(self, code, message, details):
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.details = details
|
||||
|
||||
def to_json(self):
|
||||
data = {
|
||||
"serviceFault": {
|
||||
"code": self.code,
|
||||
"message": self.message,
|
||||
"details": self.details
|
||||
}
|
||||
}
|
||||
return data
|
||||
|
||||
def __str__(self):
|
||||
return json.dumps(self.to_json(), indent=4)
|
||||
|
||||
|
||||
class BadRequest(ServiceFault):
|
||||
def __init__(self,
|
||||
validation_errors,
|
||||
code="400",
|
||||
message="Validation fault",
|
||||
details="The object is not valid"):
|
||||
ServiceFault.__init__(self, code, message, details)
|
||||
self.validation_errors = validation_errors
|
||||
|
||||
def to_json(self):
|
||||
data = {
|
||||
"badRequest": {
|
||||
"code": self.code,
|
||||
"message": self.message,
|
||||
"details": self.details,
|
||||
"validationErrors": {
|
||||
"message": self.validation_errors
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
|
||||
def __str__(self):
|
||||
return json.dumps(self.to_json(), indent=4)
|
36
lbaas_mgm/json_gearman.py
Normal file
36
lbaas_mgm/json_gearman.py
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import json
|
||||
from gearman import GearmanWorker, DataEncoder
|
||||
|
||||
|
||||
class JSONDataEncoder(DataEncoder):
|
||||
""" Class to transform data that the worker either receives or sends. """
|
||||
|
||||
@classmethod
|
||||
def encode(cls, encodable_object):
|
||||
""" Encode JSON object as string """
|
||||
return json.dumps(encodable_object)
|
||||
|
||||
@classmethod
|
||||
def decode(cls, decodable_string):
|
||||
""" Decode string to JSON object """
|
||||
return json.loads(decodable_string)
|
||||
|
||||
|
||||
class JSONGearmanWorker(GearmanWorker):
|
||||
""" Overload the Gearman worker class so we can set the data encoder. """
|
||||
data_encoder = JSONDataEncoder
|
49
lbaas_mgm/listener.py
Normal file
49
lbaas_mgm/listener.py
Normal file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import json
|
||||
import socket
|
||||
|
||||
from json_gearman import JSONGearmanWorker
|
||||
from lbaas_mgm.faults import BadRequest
|
||||
|
||||
|
||||
class Listener(object):
|
||||
def __init__(self, logger):
|
||||
self.logger = logger
|
||||
|
||||
def run(self):
|
||||
my_ip = socket.gethostbyname(socket.gethostname())
|
||||
task_name = "lbaas-mgm-%s" % my_ip
|
||||
self.logger.debug("Registering task %s" % task_name)
|
||||
|
||||
worker = JSONGearmanWorker(['localhost:4730'])
|
||||
worker.set_client_id(my_ip)
|
||||
worker.register_task(task_name, self.task)
|
||||
|
||||
def task(self, worker, job):
|
||||
data = json.loads(job.data)
|
||||
|
||||
if 'command' not in data:
|
||||
return BadRequest("Missing 'command' element").to_json()
|
||||
|
||||
command = data['command']
|
||||
self.logger.debug('Command: {cmd}'.format(cmd=command))
|
||||
if command == 'get':
|
||||
self.logger.debug('Get one node from pool')
|
||||
else:
|
||||
return BadRequest("Invalid command").to_json()
|
||||
|
||||
return data
|
87
lbaas_mgm/mgm.py
Normal file
87
lbaas_mgm/mgm.py
Normal file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import logging
|
||||
import argparse
|
||||
import daemon
|
||||
import signal
|
||||
import sys
|
||||
|
||||
from lbaas_mgm.listener import Listener
|
||||
|
||||
|
||||
class Server(object):
|
||||
def __init__(self, logger, nodes):
|
||||
self.logger = logger
|
||||
self.nodes = nodes
|
||||
|
||||
def main(self):
|
||||
self.logger.info(
|
||||
'LBaaS Pool Manager started with {nodes} nodes'
|
||||
.format(nodes=self.nodes)
|
||||
)
|
||||
signal.signal(signal.SIGINT, self.exit_handler)
|
||||
signal.signal(signal.SIGTERM, self.exit_handler)
|
||||
listner = Listener(self.logger)
|
||||
try:
|
||||
listner.run()
|
||||
except Exception as e:
|
||||
self.logger.critical(
|
||||
'Exception: {eclass}, {exception}'
|
||||
.format(eclass=e.__class__, exception=e)
|
||||
)
|
||||
self.shutdown(True)
|
||||
self.shutdown(False)
|
||||
|
||||
def exit_handler(self, signum, frame):
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
signal.signal(signal.SIGTERM, signal.SIG_IGN)
|
||||
|
||||
def shutdown(self, error):
|
||||
if not error:
|
||||
self.logger.info('Safely shutting down')
|
||||
sys.exit(0)
|
||||
else:
|
||||
self.logger.info('Shutting down due to error')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='LBaaS Node Management Daemon'
|
||||
)
|
||||
parser.add_argument('nodes', metavar='N', type=int,
|
||||
help='number of nodes to maintain')
|
||||
parser.add_argument('-d', dest='nodaemon', action='store_true',
|
||||
help='do not run in daemon mode')
|
||||
options = parser.parse_args()
|
||||
|
||||
logging.basicConfig(
|
||||
format='%(asctime)-6s: %(name)s - %(levelname)s - %(message)s',
|
||||
filename='/var/log/lbaas/lbaas_mgm.log'
|
||||
)
|
||||
logger = logging.getLogger('lbaas_mgm')
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
pid_fn = '/var/run/lbaas_mgm/lbaas_mgm.pid'
|
||||
pid = daemon.pidlockfile.TimeoutPIDLockFile(pid_fn, 10)
|
||||
|
||||
server = Server(logger, options.nodes)
|
||||
|
||||
if options.nodaemon:
|
||||
server.main()
|
||||
else:
|
||||
with daemon.DaemonContext(pidfile=pid):
|
||||
server.main()
|
7
setup.py
7
setup.py
@ -32,12 +32,13 @@ setuptools.setup(
|
||||
name="lbaas_worker",
|
||||
description="Python LBaaS Gearman Worker",
|
||||
version="1.0",
|
||||
author="David Shrewsbury",
|
||||
author_email="shrewsbury.dave@gmail.com",
|
||||
author="David Shrewsbury <shrewsbury.dave@gmail.com>, \
|
||||
Andrew Hutchings <andrew@linuxjedi.co.uk>",
|
||||
packages=setuptools.find_packages(exclude=["*.tests"]),
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'lbaas_worker = lbaas_worker.worker:main'
|
||||
'lbaas_worker = lbaas_worker.worker:main',
|
||||
'lbaas_pool_mgm = lbaas_mgm.mgm:main'
|
||||
]
|
||||
},
|
||||
cmdclass={'test': PyTest},
|
||||
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
30
tests/mock.py
Normal file
30
tests/mock.py
Normal file
@ -0,0 +1,30 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
|
||||
class FakeJob(object):
|
||||
def __init__(self, data):
|
||||
"""
|
||||
data: JSON object to convert to a string
|
||||
"""
|
||||
self.data = json.dumps(data)
|
||||
|
||||
|
||||
class MockLoggingHandler(logging.Handler):
|
||||
"""Mock logging handler to check for expected logs."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.reset()
|
||||
logging.Handler.__init__(self, *args, **kwargs)
|
||||
|
||||
def emit(self, record):
|
||||
self.messages[record.levelname.lower()].append(record.getMessage())
|
||||
|
||||
def reset(self):
|
||||
self.messages = {
|
||||
'debug': [],
|
||||
'info': [],
|
||||
'warning': [],
|
||||
'error': [],
|
||||
'critical': [],
|
||||
}
|
37
tests/test_lbaas_mgm.py
Normal file
37
tests/test_lbaas_mgm.py
Normal file
@ -0,0 +1,37 @@
|
||||
import unittest
|
||||
import logging
|
||||
|
||||
import tests.mock
|
||||
|
||||
from lbaas_mgm.listener import Listener
|
||||
|
||||
|
||||
class TestLBaaSMgmTask(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.logger = logging.getLogger('lbass_mgm_test')
|
||||
self.lh = tests.mock.MockLoggingHandler()
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
self.logger.addHandler(self.lh)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def testTaskGet(self):
|
||||
listener = Listener(self.logger)
|
||||
data = {'command': 'get'}
|
||||
job = tests.mock.FakeJob(data)
|
||||
result = listener.task(None, job)
|
||||
self.assertIn('Command: get', self.lh.messages['debug'])
|
||||
self.assertEqual(result['command'], data['command'])
|
||||
|
||||
def testTaskBad(self):
|
||||
listener = Listener(self.logger)
|
||||
data = {'command': 'bad'}
|
||||
job = tests.mock.FakeJob(data)
|
||||
result = listener.task(None, job)
|
||||
self.assertIn("badRequest", result)
|
||||
self.assertIn("validationErrors", result['badRequest'])
|
||||
self.assertEqual(
|
||||
"Invalid command",
|
||||
result['badRequest']['validationErrors']['message']
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user