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",
|
name="lbaas_worker",
|
||||||
description="Python LBaaS Gearman Worker",
|
description="Python LBaaS Gearman Worker",
|
||||||
version="1.0",
|
version="1.0",
|
||||||
author="David Shrewsbury",
|
author="David Shrewsbury <shrewsbury.dave@gmail.com>, \
|
||||||
author_email="shrewsbury.dave@gmail.com",
|
Andrew Hutchings <andrew@linuxjedi.co.uk>",
|
||||||
packages=setuptools.find_packages(exclude=["*.tests"]),
|
packages=setuptools.find_packages(exclude=["*.tests"]),
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'lbaas_worker = lbaas_worker.worker:main'
|
'lbaas_worker = lbaas_worker.worker:main',
|
||||||
|
'lbaas_pool_mgm = lbaas_mgm.mgm:main'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
cmdclass={'test': PyTest},
|
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