Initial architecture changes for using dynamic drivers and for a HAProxy driver.
This commit is contained in:
parent
d1f6c62ddd
commit
7f01bc3daf
13
libra/worker/drivers/__init__.py
Normal file
13
libra/worker/drivers/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# 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.
|
37
libra/worker/drivers/base.py
Normal file
37
libra/worker/drivers/base.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
class LoadBalancerDriver(object):
|
||||||
|
"""
|
||||||
|
Load balancer device driver base class.
|
||||||
|
|
||||||
|
This defines the API for interacting with various load balancing
|
||||||
|
appliances. Drivers for these appliances should inherit from this
|
||||||
|
class and implement the relevant API methods that it can support.
|
||||||
|
|
||||||
|
Generally, an appliance driver should queue up any changes made
|
||||||
|
via these API calls until the activate() method is called.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def bind(self, address, port):
|
||||||
|
""" Set proxy listening interface and port. """
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def add_server(self, host, port):
|
||||||
|
""" Add a server for which we will proxy. """
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def activate(self):
|
||||||
|
""" Activate any changes made. """
|
||||||
|
raise NotImplementedError()
|
13
libra/worker/drivers/haproxy/__init__.py
Normal file
13
libra/worker/drivers/haproxy/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# 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.
|
44
libra/worker/drivers/haproxy/driver.py
Normal file
44
libra/worker/drivers/haproxy/driver.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from libra.worker.drivers.base import LoadBalancerDriver
|
||||||
|
|
||||||
|
|
||||||
|
class HAProxyDriver(LoadBalancerDriver):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._config_file = '/etc/haproxy/haproxy.cfg'
|
||||||
|
self._servers = []
|
||||||
|
self.bind('0.0.0.0', 80)
|
||||||
|
|
||||||
|
def _write_config(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _restart(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
####################
|
||||||
|
# Driver API Methods
|
||||||
|
####################
|
||||||
|
|
||||||
|
def bind(self, address, port):
|
||||||
|
self._bind_address = address
|
||||||
|
self._bind_port = port
|
||||||
|
|
||||||
|
def add_server(self, host, port):
|
||||||
|
self._servers.append((host, port))
|
||||||
|
|
||||||
|
def activate(self):
|
||||||
|
self._write_config()
|
||||||
|
self._restart()
|
@ -16,7 +16,6 @@ import daemon
|
|||||||
import gearman.errors
|
import gearman.errors
|
||||||
import json
|
import json
|
||||||
import lockfile
|
import lockfile
|
||||||
import logging
|
|
||||||
import socket
|
import socket
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
@ -28,17 +27,23 @@ from libra.common.options import Options, setup_logging
|
|||||||
def lbaas_task(worker, job):
|
def lbaas_task(worker, job):
|
||||||
""" Main Gearman worker task. """
|
""" Main Gearman worker task. """
|
||||||
|
|
||||||
|
NODE_OK = "ENABLED"
|
||||||
|
NODE_ERR = "DISABLED"
|
||||||
|
|
||||||
|
logger = worker.logger
|
||||||
|
driver = worker.driver
|
||||||
|
|
||||||
# Turn string into JSON object
|
# Turn string into JSON object
|
||||||
data = json.loads(job.data)
|
data = json.loads(job.data)
|
||||||
|
|
||||||
lb_name = data['name']
|
lb_name = data['name']
|
||||||
logging.info("LB name: %s" % lb_name)
|
logger.debug("LB name: %s" % lb_name)
|
||||||
|
|
||||||
if 'nodes' not in data:
|
if 'nodes' not in data:
|
||||||
return BadRequest("Missing 'nodes' element").to_json()
|
return BadRequest("Missing 'nodes' element").to_json()
|
||||||
|
|
||||||
for lb_node in data['nodes']:
|
for lb_node in data['nodes']:
|
||||||
port, address, status = None, None, None
|
port, address = None, None
|
||||||
|
|
||||||
if 'port' in lb_node:
|
if 'port' in lb_node:
|
||||||
port = lb_node['port']
|
port = lb_node['port']
|
||||||
@ -50,24 +55,45 @@ def lbaas_task(worker, job):
|
|||||||
else:
|
else:
|
||||||
return BadRequest("Missing 'address' element.").to_json()
|
return BadRequest("Missing 'address' element.").to_json()
|
||||||
|
|
||||||
if 'status' in lb_node:
|
logger.debug("Server node: %s:%s" % (address, port))
|
||||||
status = lb_node['status']
|
|
||||||
|
|
||||||
logging.info("LB node: %s:%s - %s" % (address, port, status))
|
try:
|
||||||
lb_node['status'] = 'ACTIVE'
|
driver.add_server(address, port)
|
||||||
|
except NotImplementedError:
|
||||||
|
logger.info("Selected driver could not add server.")
|
||||||
|
lb_node['condition'] = NODE_ERR
|
||||||
|
except Exception as e:
|
||||||
|
logger.critical("Failure trying adding server: %s, %s" %
|
||||||
|
(e.__class__, e))
|
||||||
|
lb_node['condition'] = NODE_ERR
|
||||||
|
else:
|
||||||
|
lb_node['condition'] = NODE_OK
|
||||||
|
|
||||||
# Return the same JSON object, but with status fields set.
|
try:
|
||||||
|
driver.activate()
|
||||||
|
except NotImplementedError:
|
||||||
|
logger.info("Selected driver could not activate changes.")
|
||||||
|
for lb_node in data['nodes']:
|
||||||
|
lb_node['condition'] = NODE_ERR
|
||||||
|
|
||||||
|
# Return the same JSON object, but with condition fields set.
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class CustomJSONGearmanWorker(JSONGearmanWorker):
|
||||||
|
logger = None
|
||||||
|
driver = None
|
||||||
|
|
||||||
|
|
||||||
class Server(object):
|
class Server(object):
|
||||||
"""
|
"""
|
||||||
Encapsulates server activity so we can run it in either daemon or
|
Encapsulates server activity so we can run it in either daemon or
|
||||||
non-daemon mode.
|
non-daemon mode.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, logger, servers, reconnect_sleep):
|
def __init__(self, servers, reconnect_sleep):
|
||||||
self.logger = logger
|
self.logger = None
|
||||||
|
self.driver = None
|
||||||
self.servers = servers
|
self.servers = servers
|
||||||
self.reconnect_sleep = reconnect_sleep
|
self.reconnect_sleep = reconnect_sleep
|
||||||
|
|
||||||
@ -76,7 +102,7 @@ class Server(object):
|
|||||||
task_name = "lbaas-%s" % my_ip
|
task_name = "lbaas-%s" % my_ip
|
||||||
self.logger.debug("Registering task %s" % task_name)
|
self.logger.debug("Registering task %s" % task_name)
|
||||||
|
|
||||||
worker = JSONGearmanWorker(self.servers)
|
worker = CustomJSONGearmanWorker(self.servers)
|
||||||
worker.set_client_id(my_ip)
|
worker.set_client_id(my_ip)
|
||||||
worker.register_task(task_name, lbaas_task)
|
worker.register_task(task_name, lbaas_task)
|
||||||
|
|
||||||
@ -103,13 +129,22 @@ def main():
|
|||||||
|
|
||||||
options = Options('worker', 'Worker Daemon')
|
options = Options('worker', 'Worker Daemon')
|
||||||
options.parser.add_argument(
|
options.parser.add_argument(
|
||||||
'-s', dest='reconnect_sleep', type=int, metavar="TIME",
|
'-s', dest='reconnect_sleep', type=int, metavar='TIME',
|
||||||
default=60, help='seconds to sleep between job server reconnects'
|
default=60, help='seconds to sleep between job server reconnects'
|
||||||
)
|
)
|
||||||
|
options.parser.add_argument(
|
||||||
|
'--driver', dest='driver', default='haproxy.driver.HAProxyDriver',
|
||||||
|
help='Class name of device driver to use'
|
||||||
|
)
|
||||||
args = options.run()
|
args = options.run()
|
||||||
|
|
||||||
logger = setup_logging('libra_worker', args)
|
logger = setup_logging('libra_worker', args)
|
||||||
server = Server(logger, ['localhost:4730'], args.reconnect_sleep)
|
from libra.worker.drivers.haproxy.driver import HAProxyDriver
|
||||||
|
driver = HAProxyDriver()
|
||||||
|
|
||||||
|
server = Server(['localhost:4730'], args.reconnect_sleep)
|
||||||
|
server.logger = logger
|
||||||
|
server.driver = driver
|
||||||
|
|
||||||
if args.nodaemon:
|
if args.nodaemon:
|
||||||
server.main()
|
server.main()
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import unittest
|
import unittest
|
||||||
|
import mock
|
||||||
from libra.worker.worker import lbaas_task
|
from libra.worker.worker import lbaas_task
|
||||||
|
from libra.worker.drivers.base import LoadBalancerDriver
|
||||||
|
|
||||||
|
|
||||||
|
class FakeDriver(LoadBalancerDriver):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FakeJob(object):
|
class FakeJob(object):
|
||||||
@ -11,6 +18,12 @@ class FakeJob(object):
|
|||||||
self.data = json.dumps(data)
|
self.data = json.dumps(data)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeWorker(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.logger = logging.getLogger('lbaas_worker_test')
|
||||||
|
self.driver = FakeDriver()
|
||||||
|
|
||||||
|
|
||||||
class TestLBaaSTask(unittest.TestCase):
|
class TestLBaaSTask(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
pass
|
pass
|
||||||
@ -21,7 +34,7 @@ class TestLBaaSTask(unittest.TestCase):
|
|||||||
def testLBaaSTask(self):
|
def testLBaaSTask(self):
|
||||||
""" Test the lbaas_task() function """
|
""" Test the lbaas_task() function """
|
||||||
|
|
||||||
worker = None
|
worker = FakeWorker()
|
||||||
data = {
|
data = {
|
||||||
"name": "a-new-loadbalancer",
|
"name": "a-new-loadbalancer",
|
||||||
"nodes": [
|
"nodes": [
|
||||||
@ -43,15 +56,15 @@ class TestLBaaSTask(unittest.TestCase):
|
|||||||
self.assertEqual(len(r["nodes"]), 2)
|
self.assertEqual(len(r["nodes"]), 2)
|
||||||
self.assertEqual(r["nodes"][0]["address"], data["nodes"][0]["address"])
|
self.assertEqual(r["nodes"][0]["address"], data["nodes"][0]["address"])
|
||||||
self.assertEqual(r["nodes"][0]["port"], data["nodes"][0]["port"])
|
self.assertEqual(r["nodes"][0]["port"], data["nodes"][0]["port"])
|
||||||
self.assertIn("status", r["nodes"][0])
|
self.assertIn("condition", r["nodes"][0])
|
||||||
self.assertEqual(r["nodes"][1]["address"], data["nodes"][1]["address"])
|
self.assertEqual(r["nodes"][1]["address"], data["nodes"][1]["address"])
|
||||||
self.assertEqual(r["nodes"][1]["port"], data["nodes"][1]["port"])
|
self.assertEqual(r["nodes"][1]["port"], data["nodes"][1]["port"])
|
||||||
self.assertIn("status", r["nodes"][1])
|
self.assertIn("condition", r["nodes"][1])
|
||||||
|
|
||||||
def testMissingNodes(self):
|
def testMissingNodes(self):
|
||||||
""" Test invalid messages: missing nodes """
|
""" Test invalid messages: missing nodes """
|
||||||
|
|
||||||
worker = None
|
worker = FakeWorker()
|
||||||
data = {
|
data = {
|
||||||
"name": "a-new-loadbalancer"
|
"name": "a-new-loadbalancer"
|
||||||
}
|
}
|
||||||
@ -63,7 +76,7 @@ class TestLBaaSTask(unittest.TestCase):
|
|||||||
def testMissingPort(self):
|
def testMissingPort(self):
|
||||||
""" Test invalid messages: missing port """
|
""" Test invalid messages: missing port """
|
||||||
|
|
||||||
worker = None
|
worker = FakeWorker()
|
||||||
data = {
|
data = {
|
||||||
"name": "a-new-loadbalancer",
|
"name": "a-new-loadbalancer",
|
||||||
"nodes": [
|
"nodes": [
|
||||||
@ -80,7 +93,7 @@ class TestLBaaSTask(unittest.TestCase):
|
|||||||
def testMissingAddress(self):
|
def testMissingAddress(self):
|
||||||
""" Test invalid messages: missing address """
|
""" Test invalid messages: missing address """
|
||||||
|
|
||||||
worker = None
|
worker = FakeWorker()
|
||||||
data = {
|
data = {
|
||||||
"name": "a-new-loadbalancer",
|
"name": "a-new-loadbalancer",
|
||||||
"nodes": [
|
"nodes": [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user