API+ADMIN_API: SSL and DB fixes
* Add SSL to Admin API * Improve debug flag on both APIs * Make Admin API use its own model * Improve DB session handling and rollbacks Change-Id: I9d268079effbaca70c8c69d591bf48d494c1293f
This commit is contained in:
parent
971f37d680
commit
df060ccd19
@ -76,6 +76,8 @@ poll_timeout_retry = 30
|
||||
db_user=root
|
||||
db_pass=passwd
|
||||
db_schema=lbaas
|
||||
ssl_certfile=certfile.crt
|
||||
ssl_keyfile=keyfile.key
|
||||
|
||||
[api]
|
||||
host=0.0.0.0
|
||||
|
@ -46,6 +46,10 @@ def setup_app(pecan_config, args):
|
||||
'host': args.db_host,
|
||||
'schema': args.db_schema
|
||||
}
|
||||
if args.debug:
|
||||
config['wsme'] = {'debug': True}
|
||||
config['app']['debug'] = True
|
||||
|
||||
pecan.configuration.set_config(config, overwrite=True)
|
||||
|
||||
app = pecan.make_app(
|
||||
@ -93,11 +97,20 @@ def main():
|
||||
options.parser.add_argument(
|
||||
'--db_schema', help='MySQL schema for libra'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--ssl_cert',
|
||||
help='Path to an SSL certificate file'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--ssl_keyfile',
|
||||
help='Path to an SSL key file'
|
||||
)
|
||||
|
||||
args = options.run()
|
||||
|
||||
required_args = [
|
||||
'db_user', 'db_pass', 'db_host', 'db_schema'
|
||||
'db_user', 'db_pass', 'db_host', 'db_schema', 'ssl_certfile',
|
||||
'ssl_keyfile'
|
||||
]
|
||||
|
||||
missing_args = 0
|
||||
@ -131,6 +144,15 @@ def main():
|
||||
logger.info('Starting on {0}:{1}'.format(args.host, args.port))
|
||||
api = setup_app(pc, args)
|
||||
sys.stderr = LogStdout(logger)
|
||||
wsgi.server(eventlet.listen((args.host, args.port)), api)
|
||||
# TODO: set ca_certs and cert_reqs=CERT_REQUIRED
|
||||
wsgi.server(
|
||||
eventlet.wrap_ssl(
|
||||
eventlet.listen((args.host, args.port)),
|
||||
certfile=args.ssl_certfile,
|
||||
keyfile=args.ssl_keyfile,
|
||||
server_side=True
|
||||
),
|
||||
api
|
||||
)
|
||||
|
||||
return 0
|
||||
|
@ -19,31 +19,8 @@ app = {
|
||||
'modules': ['libra.admin_api'],
|
||||
'static_root': '%(confdir)s/public',
|
||||
'template_path': '%(confdir)s/admin_api/templates',
|
||||
'debug': True,
|
||||
'errors': {
|
||||
404: '/notfound',
|
||||
'__force_dict__': True
|
||||
}
|
||||
}
|
||||
|
||||
wsme = {
|
||||
'debug': True
|
||||
}
|
||||
|
||||
#database = {
|
||||
# 'username': 'root',
|
||||
# 'password': 'testaburger',
|
||||
# 'host': 'localhost',
|
||||
# 'schema': 'lbaas'
|
||||
#}
|
||||
|
||||
#gearman = {
|
||||
# 'server': ['localhost:4730'],
|
||||
#}
|
||||
|
||||
# Custom Configurations must be in Python dictionary format::
|
||||
#
|
||||
# foo = {'bar':'baz'}
|
||||
#
|
||||
# All configurations are accessible at::
|
||||
# pecan.conf
|
||||
|
@ -21,8 +21,8 @@ import wsmeext.pecan as wsme_pecan
|
||||
from wsme.exc import ClientSideError
|
||||
from usage import UsageController
|
||||
from libra.admin_api.model.validators import DeviceResp, DevicePost, DevicePut
|
||||
from libra.api.model.lbaas import LoadBalancer, Device, session
|
||||
from libra.api.model.lbaas import loadbalancers_devices
|
||||
from libra.admin_api.model.lbaas import LoadBalancer, Device, session
|
||||
from libra.admin_api.model.lbaas import loadbalancers_devices
|
||||
|
||||
|
||||
class DevicesController(RestController):
|
||||
|
@ -14,7 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
from pecan import expose, response
|
||||
from libra.api.model.lbaas import Device, session
|
||||
from libra.admin_api.model.lbaas import Device, session
|
||||
from libra.admin_api.model.responses import Responses
|
||||
from pecan.rest import RestController
|
||||
|
||||
|
@ -59,6 +59,10 @@ def setup_app(pecan_config, args):
|
||||
config['gearman'] = {
|
||||
'server': args.gearman
|
||||
}
|
||||
if args.debug:
|
||||
config['wsme'] = {'debug': True}
|
||||
config['app']['debug'] = True
|
||||
|
||||
pecan.configuration.set_config(config, overwrite=True)
|
||||
|
||||
app = pecan.make_app(
|
||||
|
@ -19,31 +19,8 @@ app = {
|
||||
'modules': ['libra.api'],
|
||||
'static_root': '%(confdir)s/public',
|
||||
'template_path': '%(confdir)s/api/templates',
|
||||
'debug': True,
|
||||
'errors': {
|
||||
404: '/notfound',
|
||||
'__force_dict__': True
|
||||
}
|
||||
}
|
||||
|
||||
#wsme = {
|
||||
# 'debug': True
|
||||
#}
|
||||
|
||||
#database = {
|
||||
# 'username': 'root',
|
||||
# 'password': 'testaburger',
|
||||
# 'host': 'localhost',
|
||||
# 'schema': 'lbaas'
|
||||
#}
|
||||
|
||||
#gearman = {
|
||||
# 'server': ['localhost:4730'],
|
||||
#}
|
||||
|
||||
# Custom Configurations must be in Python dictionary format::
|
||||
#
|
||||
# foo = {'bar':'baz'}
|
||||
#
|
||||
# All configurations are accessible at::
|
||||
# pecan.conf
|
||||
|
@ -15,16 +15,18 @@
|
||||
|
||||
from pecan import expose
|
||||
from pecan.rest import RestController
|
||||
from libra.api.model.lbaas import Limits, session
|
||||
from libra.api.model.lbaas import Limits, get_session
|
||||
|
||||
|
||||
class LimitsController(RestController):
|
||||
@expose('json')
|
||||
def get(self):
|
||||
resp = {}
|
||||
session = get_session()
|
||||
limits = session.query(Limits).all()
|
||||
for limit in limits:
|
||||
resp[limit.name] = limit.value
|
||||
|
||||
resp = {"limits": {"absolute": {"values": resp}}}
|
||||
session.rollback()
|
||||
return resp
|
||||
|
@ -25,7 +25,7 @@ from nodes import NodesController
|
||||
from virtualips import VipsController
|
||||
from logs import LogsController
|
||||
# models
|
||||
from libra.api.model.lbaas import LoadBalancer, Device, Node, session
|
||||
from libra.api.model.lbaas import LoadBalancer, Device, Node, get_session
|
||||
from libra.api.model.lbaas import loadbalancers_devices, Limits
|
||||
from libra.api.model.validators import LBPut, LBPost, LBResp, LBVipResp
|
||||
from libra.api.model.validators import LBRespNode
|
||||
@ -58,7 +58,7 @@ class LoadBalancersController(RestController):
|
||||
"""
|
||||
|
||||
tenant_id = get_limited_to_project(request.headers)
|
||||
|
||||
session = get_session()
|
||||
# if we don't have an id then we want a list of them own by this tenent
|
||||
if not load_balancer_id:
|
||||
lbs = session.query(
|
||||
@ -177,7 +177,7 @@ class LoadBalancersController(RestController):
|
||||
raise ClientSideError(
|
||||
'IP Address {0} not valid'.format(node.address)
|
||||
)
|
||||
|
||||
session = get_session()
|
||||
lblimit = session.query(Limits.value).\
|
||||
filter(Limits.name == 'maxLoadBalancers').scalar()
|
||||
nodelimit = session.query(Limits.value).\
|
||||
@ -189,16 +189,19 @@ class LoadBalancersController(RestController):
|
||||
filter(LoadBalancer.status != 'DELETED').count()
|
||||
|
||||
if len(body.name) > namelimit:
|
||||
session.rollback()
|
||||
raise ClientSideError(
|
||||
'Length of Load Balancer name too long'
|
||||
)
|
||||
# TODO: this should probably be a 413, not sure how to do that yet
|
||||
if count >= lblimit:
|
||||
session.rollback()
|
||||
raise OverLimit(
|
||||
'Account has hit limit of {0} Load Balancers'.
|
||||
format(lblimit)
|
||||
)
|
||||
if len(body.nodes) > nodelimit:
|
||||
session.rollback()
|
||||
raise OverLimit(
|
||||
'Too many backend nodes supplied (limit is {0}'.
|
||||
format(nodelimit)
|
||||
@ -230,6 +233,7 @@ class LoadBalancersController(RestController):
|
||||
filter(Device.id == virtual_id).\
|
||||
first()
|
||||
if old_lb is None:
|
||||
session.rollback()
|
||||
raise InvalidInput(
|
||||
'virtualIps', virtual_id, 'Invalid virtual IP provided'
|
||||
)
|
||||
@ -243,6 +247,7 @@ class LoadBalancersController(RestController):
|
||||
filter(LoadBalancer.protocol == 'HTTP').\
|
||||
count()
|
||||
if old_count:
|
||||
session.rollback()
|
||||
# Error here, can have only one HTTP
|
||||
raise ClientSideError(
|
||||
'Only one HTTP load balancer allowed per device'
|
||||
@ -256,12 +261,14 @@ class LoadBalancersController(RestController):
|
||||
filter(LoadBalancer.protocol == 'TCP').\
|
||||
count()
|
||||
if old_count:
|
||||
session.rollback()
|
||||
# Error here, can have only one TCP
|
||||
raise ClientSideError(
|
||||
'Only one TCP load balancer allowed per device'
|
||||
)
|
||||
|
||||
if device is None:
|
||||
session.rollback()
|
||||
raise RuntimeError('No devices available')
|
||||
|
||||
lb.tenantid = tenant_id
|
||||
@ -354,7 +361,7 @@ class LoadBalancersController(RestController):
|
||||
raise ClientSideError('Load Balancer ID is required')
|
||||
|
||||
tenant_id = get_limited_to_project(request.headers)
|
||||
|
||||
session = get_session()
|
||||
# grab the lb
|
||||
lb = session.query(LoadBalancer).\
|
||||
filter(LoadBalancer.id == self.lbid).\
|
||||
@ -362,12 +369,14 @@ class LoadBalancersController(RestController):
|
||||
filter(LoadBalancer.status != 'DELETED').first()
|
||||
|
||||
if lb is None:
|
||||
session.rollback()
|
||||
raise ClientSideError('Load Balancer ID is not valid')
|
||||
|
||||
if body.name != Unset:
|
||||
namelimit = session.query(Limits.value).\
|
||||
filter(Limits.name == 'maxLoadBalancerNameLength').scalar()
|
||||
if len(body.name) > namelimit:
|
||||
session.rollback()
|
||||
raise ClientSideError(
|
||||
'Length of Load Balancer name too long'
|
||||
)
|
||||
@ -405,12 +414,14 @@ class LoadBalancersController(RestController):
|
||||
"""
|
||||
tenant_id = get_limited_to_project(request.headers)
|
||||
# grab the lb
|
||||
session = get_session()
|
||||
lb = session.query(LoadBalancer).\
|
||||
filter(LoadBalancer.id == load_balancer_id).\
|
||||
filter(LoadBalancer.tenantid == tenant_id).\
|
||||
filter(LoadBalancer.status != 'DELETED').first()
|
||||
|
||||
if lb is None:
|
||||
session.rollback()
|
||||
response.status = 400
|
||||
return dict(
|
||||
faultcode="Client",
|
||||
@ -435,6 +446,7 @@ class LoadBalancersController(RestController):
|
||||
response.status = 202
|
||||
return ''
|
||||
except:
|
||||
session.rollback()
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.exception('Error communicating with load balancer pool')
|
||||
response.status = 500
|
||||
|
@ -19,7 +19,7 @@ from pecan import conf
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
from wsme.exc import ClientSideError
|
||||
from wsme import Unset
|
||||
from libra.api.model.lbaas import LoadBalancer, Device, session
|
||||
from libra.api.model.lbaas import LoadBalancer, Device, get_session
|
||||
from libra.api.acl import get_limited_to_project
|
||||
from libra.api.model.validators import LBLogsPost
|
||||
from libra.api.library.gearman_client import submit_job
|
||||
@ -35,13 +35,14 @@ class LogsController(RestController):
|
||||
raise ClientSideError('Load Balancer ID has not been supplied')
|
||||
|
||||
tenant_id = get_limited_to_project(request.headers)
|
||||
|
||||
session = get_session()
|
||||
load_balancer = session.query(LoadBalancer).\
|
||||
filter(LoadBalancer.tenantid == tenant_id).\
|
||||
filter(LoadBalancer.id == self.lbid).\
|
||||
filter(LoadBalancer.status != 'DELETED').\
|
||||
first()
|
||||
if load_balancer is None:
|
||||
session.rollback()
|
||||
raise ClientSideError('Load Balancer not found')
|
||||
|
||||
load_balancer.status = 'PENDING_UPDATE'
|
||||
|
@ -20,7 +20,8 @@ import wsmeext.pecan as wsme_pecan
|
||||
from wsme.exc import ClientSideError
|
||||
from wsme import Unset
|
||||
#default response objects
|
||||
from libra.api.model.lbaas import LoadBalancer, Node, session, Limits, Device
|
||||
from libra.api.model.lbaas import LoadBalancer, Node, get_session, Limits
|
||||
from libra.api.model.lbaas import Device
|
||||
from libra.api.acl import get_limited_to_project
|
||||
from libra.api.model.validators import LBNodeResp, LBNodePost, NodeResp
|
||||
from libra.api.model.validators import LBNodePut
|
||||
@ -56,7 +57,7 @@ class NodesController(RestController):
|
||||
faultcode='Client',
|
||||
faultstring='Load Balancer ID not supplied'
|
||||
)
|
||||
|
||||
session = get_session()
|
||||
if not node_id:
|
||||
nodes = session.query(
|
||||
Node.id, Node.address, Node.port, Node.status, Node.enabled
|
||||
@ -132,13 +133,14 @@ class NodesController(RestController):
|
||||
raise ClientSideError(
|
||||
'IP Address {0} not valid'.format(node.address)
|
||||
)
|
||||
|
||||
session = get_session()
|
||||
load_balancer = session.query(LoadBalancer).\
|
||||
filter(LoadBalancer.tenantid == tenant_id).\
|
||||
filter(LoadBalancer.id == self.lbid).\
|
||||
filter(LoadBalancer.status != 'DELETED').\
|
||||
first()
|
||||
if load_balancer is None:
|
||||
session.rollback()
|
||||
raise ClientSideError('Load Balancer not found')
|
||||
|
||||
load_balancer.status = 'PENDING_UPDATE'
|
||||
@ -149,6 +151,7 @@ class NodesController(RestController):
|
||||
filter(Node.lbid == self.lbid).count()
|
||||
|
||||
if (nodecount + len(body.nodes)) > nodelimit:
|
||||
session.rollback()
|
||||
raise OverLimit(
|
||||
'Command would exceed Load Balancer node limit'
|
||||
)
|
||||
@ -195,6 +198,7 @@ class NodesController(RestController):
|
||||
raise ClientSideError('Node ID has not been supplied')
|
||||
|
||||
tenant_id = get_limited_to_project(request.headers)
|
||||
session = get_session()
|
||||
# grab the lb
|
||||
lb = session.query(LoadBalancer).\
|
||||
filter(LoadBalancer.id == self.lbid).\
|
||||
@ -202,6 +206,7 @@ class NodesController(RestController):
|
||||
filter(LoadBalancer.status != 'DELETED').first()
|
||||
|
||||
if lb is None:
|
||||
session.rollback()
|
||||
raise ClientSideError('Load Balancer ID is not valid')
|
||||
|
||||
node = session.query(Node).\
|
||||
@ -209,6 +214,7 @@ class NodesController(RestController):
|
||||
filter(Node.id == self.nodeid).first()
|
||||
|
||||
if node is None:
|
||||
session.rollback()
|
||||
raise ClientSideError('Node ID is not valid')
|
||||
|
||||
if body.condition != Unset:
|
||||
@ -250,12 +256,14 @@ class NodesController(RestController):
|
||||
)
|
||||
|
||||
tenant_id = get_limited_to_project(request.headers)
|
||||
session = get_session()
|
||||
load_balancer = session.query(LoadBalancer).\
|
||||
filter(LoadBalancer.tenantid == tenant_id).\
|
||||
filter(LoadBalancer.id == self.lbid).\
|
||||
filter(LoadBalancer.device != 'DELETED').\
|
||||
first()
|
||||
if load_balancer is None:
|
||||
session.rollback()
|
||||
response.status = 400
|
||||
return dict(
|
||||
faultcode="Client",
|
||||
@ -266,16 +274,18 @@ class NodesController(RestController):
|
||||
filter(Node.lbid == self.lbid).count()
|
||||
# Can't delete the last LB
|
||||
if nodecount <= 1:
|
||||
session.rollback()
|
||||
response.status = 400
|
||||
return dict(
|
||||
faultcode="Client",
|
||||
faultstring="Load Balancer not found"
|
||||
faultstring="Cannot delete the last node in a load balancer"
|
||||
)
|
||||
node = session.query(Node).\
|
||||
filter(Node.lbid == self.lbid).\
|
||||
filter(Node.id == node_id).\
|
||||
first()
|
||||
if not node:
|
||||
session.rollback()
|
||||
response.status = 400
|
||||
return dict(
|
||||
faultcode="Client",
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
from pecan import response, expose, request
|
||||
from pecan.rest import RestController
|
||||
from libra.api.model.lbaas import LoadBalancer, Device, session
|
||||
from libra.api.model.lbaas import LoadBalancer, Device, get_session
|
||||
from libra.api.acl import get_limited_to_project
|
||||
|
||||
|
||||
@ -41,6 +41,7 @@ class VipsController(RestController):
|
||||
faultcode="Client",
|
||||
faultstring="Load Balancer ID not provided"
|
||||
)
|
||||
session = get_session()
|
||||
device = session.query(
|
||||
Device.id, Device.floatingIpAddr
|
||||
).join(LoadBalancer.devices).\
|
||||
@ -48,6 +49,7 @@ class VipsController(RestController):
|
||||
filter(LoadBalancer.tenantid == tenant_id).first()
|
||||
|
||||
if not device:
|
||||
session.rollback()
|
||||
response.status = 400
|
||||
return dict(
|
||||
faultcode="Client",
|
||||
@ -61,5 +63,5 @@ class VipsController(RestController):
|
||||
"ipVersion": "IPV4"
|
||||
}]
|
||||
}
|
||||
|
||||
session.rollback()
|
||||
return resp
|
||||
|
@ -16,7 +16,7 @@ import eventlet
|
||||
eventlet.monkey_patch()
|
||||
import logging
|
||||
from libra.common.json_gearman import JSONGearmanClient
|
||||
from libra.api.model.lbaas import LoadBalancer, session, Device
|
||||
from libra.api.model.lbaas import LoadBalancer, get_session, Device
|
||||
from pecan import conf
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ class GearmanClientThread(object):
|
||||
self.gearman_client = JSONGearmanClient(conf.gearman.server)
|
||||
|
||||
def send_delete(self, data):
|
||||
session = get_session()
|
||||
count = session.query(
|
||||
LoadBalancer
|
||||
).join(LoadBalancer.devices).\
|
||||
@ -100,7 +101,7 @@ class GearmanClientThread(object):
|
||||
filter(LoadBalancer.id == self.lbid).\
|
||||
first()
|
||||
if not status:
|
||||
self._set_error(data, response)
|
||||
self._set_error(data, response, session)
|
||||
else:
|
||||
lb.status = 'DELETED'
|
||||
if count == 0:
|
||||
@ -111,7 +112,7 @@ class GearmanClientThread(object):
|
||||
device.status = 'OFFLINE'
|
||||
session.commit()
|
||||
|
||||
def _set_error(self, device_id, errmsg):
|
||||
def _set_error(self, device_id, errmsg, session):
|
||||
lbs = session.query(
|
||||
LoadBalancer
|
||||
).join(LoadBalancer.nodes).\
|
||||
@ -128,6 +129,7 @@ class GearmanClientThread(object):
|
||||
lb.errmsg = errmsg
|
||||
|
||||
def send_archive(self, data):
|
||||
session = get_session()
|
||||
lb = session.query(LoadBalancer).\
|
||||
filter(LoadBalancer.id == self.lbid).\
|
||||
first()
|
||||
@ -155,6 +157,7 @@ class GearmanClientThread(object):
|
||||
session.commit()
|
||||
|
||||
def send_update(self, data):
|
||||
session = get_session()
|
||||
lbs = session.query(
|
||||
LoadBalancer
|
||||
).join(LoadBalancer.nodes).\
|
||||
@ -190,7 +193,7 @@ class GearmanClientThread(object):
|
||||
filter(LoadBalancer.id == self.lbid).\
|
||||
first()
|
||||
if not status:
|
||||
self._set_error(data, response)
|
||||
self._set_error(data, response, session)
|
||||
else:
|
||||
lb.status = 'ACTIVE'
|
||||
session.commit()
|
||||
|
@ -113,5 +113,6 @@ class Node(DeclarativeBase):
|
||||
weight = Column(u'weight', INTEGER(), nullable=False)
|
||||
|
||||
|
||||
"""session"""
|
||||
def get_session():
|
||||
session = sessionmaker(bind=engine)()
|
||||
return session
|
||||
|
Loading…
x
Reference in New Issue
Block a user