API+STATSD: Fixes

Statsd:
* Increase retry timeout to 30 seconds to work around weird worker pause

API:
* Add modify and node-modify support (and kludge to wsme to make it work)
* Fix status codes for wsme (currently requires trunk version of wsme)

Change-Id: Ib5f30b0d6306c4105fd30a6ca588a89ba96d29ee
This commit is contained in:
Andrew Hutchings 2013-06-05 17:45:27 +01:00
parent e7657309da
commit b8e7a919ba
5 changed files with 113 additions and 10 deletions

View File

@ -25,12 +25,15 @@ from virtualips import VipsController
# models
from libra.api.model.lbaas import LoadBalancer, Device, Node, session
from libra.api.model.lbaas import loadbalancers_devices, Limits
from libra.api.model.validators import LBPost, LBResp, LBVipResp, LBNode
from libra.api.model.validators import LBPut, LBPost, LBResp, LBVipResp, LBNode
from libra.api.library.gearman_client import submit_job
from libra.api.acl import get_limited_to_project
class LoadBalancersController(RestController):
def __init__(self, lbid=None):
self.lbid = lbid
@expose('json')
def get(self, load_balancer_id=None):
"""Fetches a list of load balancers or the details of one balancer if
@ -116,7 +119,7 @@ class LoadBalancersController(RestController):
response.status = 200
return load_balancers
@wsme_pecan.wsexpose(LBResp, body=LBPost, status=202)
@wsme_pecan.wsexpose(LBResp, body=LBPost, status_code=202)
def post(self, body=None):
"""Accepts edit if load_balancer_id isn't blank or create load balancer
posts.
@ -305,6 +308,40 @@ class LoadBalancersController(RestController):
session.rollback()
raise ClientSideError(errstr)
@wsme_pecan.wsexpose(None, body=LBPut, status_code=202)
def put(self, body=None):
if not self.lbid:
raise ClientSideError('Load Balancer ID is required')
tenant_id = get_limited_to_project(request.headers)
# grab the lb
lb = session.query(LoadBalancer).\
filter(LoadBalancer.id == self.lbid).\
filter(LoadBalancer.tenantid == tenant_id).\
filter(LoadBalancer.status != 'DELETED').first()
if lb is None:
raise ClientSideError('Load Balancer ID is not valid')
if body.name != Unset:
lb.name = body.name
if body.algorithm != Unset:
lb.algorithm = body.algorithm
lb.status = 'PENDING_UPDATE'
device = session.query(
Device.id, Device.name
).join(LoadBalancer.devices).\
filter(LoadBalancer.id == self.lbid).\
first()
session.commit()
submit_job(
'UPDATE', device.name, device.id, lb.id
)
return
@expose('json')
def delete(self, load_balancer_id):
"""Remove a load balancer from the account.
@ -386,5 +423,7 @@ class LoadBalancersController(RestController):
return NodesController(lbid), remainder[1:]
if remainder[0] == 'virtualips':
return VipsController(lbid), remainder[1:]
# Kludgy fix for PUT since WSME doesn't like IDs on the path
elif lbid:
return LoadBalancersController(lbid), remainder
abort(404)

View File

@ -13,21 +13,24 @@
# License for the specific language governing permissions and limitations
# under the License.
from pecan import expose, response, request
from pecan import expose, response, request, abort
from pecan.rest import RestController
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.acl import get_limited_to_project
from libra.api.model.validators import LBNodeResp, LBNodePost, NodeResp
from libra.api.model.validators import LBNodePut
from libra.api.library.gearman_client import submit_job
class NodesController(RestController):
"""Functions for /loadbalancers/{load_balancer_id}/nodes/* routing"""
def __init__(self, lbid):
def __init__(self, lbid, nodeid=None):
self.lbid = lbid
self.nodeid = nodeid
@expose('json')
def get(self, node_id=None):
@ -89,7 +92,7 @@ class NodesController(RestController):
response.status = 200
return node_response
@wsme_pecan.wsexpose(LBNodeResp, body=LBNodePost, status=202)
@wsme_pecan.wsexpose(LBNodeResp, body=LBNodePost, status_code=202)
def post(self, body=None):
"""Adds a new node to the load balancer OR Modify the configuration
of a node on the load balancer.
@ -166,6 +169,45 @@ class NodesController(RestController):
)
return return_data
@wsme_pecan.wsexpose(None, body=LBNodePut, status_code=202)
def put(self, body=None):
if not self.lbid:
raise ClientSideError('Load Balancer ID has not been supplied')
if not self.nodeid:
raise ClientSideError('Node ID has not been supplied')
tenant_id = get_limited_to_project(request.headers)
# grab the lb
lb = session.query(LoadBalancer).\
filter(LoadBalancer.id == self.lbid).\
filter(LoadBalancer.tenantid == tenant_id).\
filter(LoadBalancer.status != 'DELETED').first()
if lb is None:
raise ClientSideError('Load Balancer ID is not valid')
node = session.query(Node).\
filter(Node.lbid == self.lbid).\
filter(Node.id == self.nodeid).first()
if node is None:
raise ClientSideError('Node ID is not valid')
if body.condition != Unset:
node.condition = body.condition
lb.status = 'PENDING_UPDATE'
device = session.query(
Device.id, Device.name
).join(LoadBalancer.devices).\
filter(LoadBalancer.id == self.lbid).\
first()
session.commit()
submit_job(
'UPDATE', device.name, device.id, lb.id
)
return
@expose('json')
def delete(self, node_id):
"""Remove a node from the load balancer.
@ -230,3 +272,14 @@ class NodesController(RestController):
)
response.status = 202
return None
@expose('json')
def _lookup(self, nodeid, *remainder):
"""Routes more complex url mapping.
Raises: 404
"""
# Kludgy fix for PUT since WSME doesn't like IDs on the path
if nodeid:
return NodesController(self.lbid, nodeid), remainder
abort(404)

View File

@ -74,6 +74,7 @@ class GearmanClientThread(object):
'loadBalancers': [{
'name': keep_lb.name,
'protocol': keep_lb.protocol,
'algorithm': keep_lb.algorithm,
'port': keep_lb.port,
'nodes': []
}]
@ -139,6 +140,7 @@ class GearmanClientThread(object):
lb_data = {
'name': lb.name,
'protocol': lb.protocol,
'algorithm': lb.algorithm,
'port': lb.port,
'nodes': []
}

View File

@ -15,13 +15,17 @@
from wsme import types as wtypes
from wsme import wsattr
from wsme.types import Base
from wsme.types import Base, Enum
class LBNode(Base):
port = wsattr(int, mandatory=True)
address = wsattr(wtypes.text, mandatory=True)
condition = wtypes.text
condition = Enum(wtypes.text, 'ENABLED', 'DISABLED')
class LBNodePut(Base):
condition = Enum(wtypes.text, 'ENABLED', 'DISABLED')
class NodeResp(Base):
@ -48,11 +52,16 @@ class LBPost(Base):
name = wsattr(wtypes.text, mandatory=True)
nodes = wsattr(['LBNode'], mandatory=True)
protocol = wtypes.text
algorithm = wtypes.text
algorithm = Enum(wtypes.text, 'ROUND_ROBIN', 'LEAST_CONNECTIONS')
port = int
virtualIps = wsattr(['LBVip'])
class LBPut(Base):
name = wtypes.text
algorithm = Enum(wtypes.text, 'ROUND_ROBIN', 'LEAST_CONNECTIONS')
class LBVipResp(Base):
id = int
address = wtypes.text

View File

@ -60,7 +60,7 @@ class GearJobs(object):
list_of_jobs.append(dict(task=str(node), data=job_data))
submitted_pings = self.gm_client.submit_multiple_jobs(
list_of_jobs, background=False, wait_until_complete=True,
poll_timeout=10.0
poll_timeout=30.0
)
for ping in submitted_pings:
if ping.state == 'UNKNOWN':