diff --git a/libra/api/controllers/load_balancers.py b/libra/api/controllers/load_balancers.py index 9c44fc9a..1e29e347 100644 --- a/libra/api/controllers/load_balancers.py +++ b/libra/api/controllers/load_balancers.py @@ -32,7 +32,7 @@ from libra.common.api.lbaas import loadbalancers_devices, Limits, Vip, Ports from libra.common.api.lbaas import HealthMonitor from libra.common.exc import ExhaustedError from libra.api.model.validators import LBPut, LBPost, LBResp, LBVipResp -from libra.api.model.validators import LBRespNode +from libra.api.model.validators import LBRespNode, LBOptions from libra.common.api.gearman_client import submit_job from libra.api.acl import get_limited_to_project from libra.api.library.exp import OverLimit, IPOutOfRange, NotFound @@ -44,6 +44,12 @@ from wsme import types as wtypes class LoadBalancersController(RestController): + + LB_TIMEOUT_MS = 30000 + LB_TIMEOUT_MAX = 1000000 + LB_RETRIES = 3 + LB_RETRIES_MAX = 256 + def __init__(self, lbid=None): self.lbid = lbid @@ -77,7 +83,8 @@ class LoadBalancersController(RestController): LoadBalancer.protocol, LoadBalancer.port, LoadBalancer.algorithm, LoadBalancer.status, LoadBalancer.created, - LoadBalancer.updated + LoadBalancer.updated, LoadBalancer.timeout, + LoadBalancer.retries ).filter(LoadBalancer.tenantid == tenant_id).\ filter(LoadBalancer.status == 'DELETED').all() else: @@ -86,7 +93,8 @@ class LoadBalancersController(RestController): LoadBalancer.protocol, LoadBalancer.port, LoadBalancer.algorithm, LoadBalancer.status, LoadBalancer.created, - LoadBalancer.updated + LoadBalancer.updated, LoadBalancer.timeout, + LoadBalancer.retries ).filter(LoadBalancer.tenantid == tenant_id).\ filter(LoadBalancer.status != 'DELETED').all() load_balancers = {'loadBalancers': []} @@ -96,6 +104,20 @@ class LoadBalancersController(RestController): lb['nodeCount'] = session.query(Node).\ filter(Node.lbid == lb['id']).count() lb['id'] = str(lb['id']) + + # Unset options get set to default values + lb['options'] = {} + if lb['timeout']: + lb['options']['timeout'] = lb['timeout'] + else: + lb['options']['timeout'] = self.LB_TIMEOUT_MS + if lb['retries']: + lb['options']['retries'] = lb['retries'] + else: + lb['options']['retries'] = self.LB_RETRIES + del(lb['timeout']) + del(lb['retries']) + load_balancers['loadBalancers'].append(lb) else: load_balancers = session.query( @@ -103,6 +125,7 @@ class LoadBalancersController(RestController): LoadBalancer.port, LoadBalancer.algorithm, LoadBalancer.status, LoadBalancer.created, LoadBalancer.updated, LoadBalancer.errmsg, + LoadBalancer.timeout, LoadBalancer.retries, Vip.id.label('vipid'), Vip.ip ).join(LoadBalancer.devices).\ outerjoin(Device.vip).\ @@ -168,6 +191,21 @@ class LoadBalancersController(RestController): del node['weight'] load_balancers['nodes'].append(node) + # Unset options get set to default values + load_balancers['options'] = {} + if load_balancers['timeout']: + load_balancers['options']['timeout'] =\ + load_balancers['timeout'] + else: + load_balancers['options']['timeout'] = self.LB_TIMEOUT_MS + if load_balancers['retries']: + load_balancers['options']['retries'] =\ + load_balancers['retries'] + else: + load_balancers['options']['retries'] = self.LB_RETRIES + del(load_balancers['timeout']) + del(load_balancers['retries']) + session.rollback() response.status = 200 return load_balancers @@ -252,19 +290,33 @@ class LoadBalancersController(RestController): num_galera_primary_nodes += 1 # Options defaults - client_timeout_ms = 30000 - server_timeout_ms = 30000 - connect_timeout_ms = 30000 - connect_retries = 3 + timeout_ms = self.LB_TIMEOUT_MS + retries = self.LB_RETRIES if body.options: - if body.options.client_timeout != Unset: - client_timeout_ms = body.options.client_timeout - if body.options.server_timeout != Unset: - server_timeout_ms = body.options.server_timeout - if body.options.connect_timeout != Unset: - connect_timeout_ms = body.options.connect_timeout - if body.options.connect_retries != Unset: - connect_retries = body.options.connect_retries + if body.options.timeout != Unset: + try: + timeout_ms = int(body.options.timeout) + if timeout_ms < 0 or timeout_ms > self.LB_TIMEOUT_MAX: + raise ClientSideError( + 'timeout must be between 0 and {0} ms' + .format(self.LB_TIMEOUT_MAX) + ) + except ValueError: + raise ClientSideError( + 'timeout must be an integer' + ) + if body.options.retries != Unset: + try: + retries = int(body.options.retries) + if retries < 0 or retries > self.LB_RETRIES_MAX: + raise ClientSideError( + 'retries must be between 0 and {0}' + .format(self.LB_RETRIES_MAX) + ) + except ValueError: + raise ClientSideError( + 'retries must be an integer' + ) # Galera sanity checks if is_galera and num_galera_primary_nodes != 1: @@ -431,10 +483,8 @@ class LoadBalancersController(RestController): else: lb.algorithm = 'ROUND_ROBIN' - lb.client_timeout = client_timeout_ms - lb.server_timeout = server_timeout_ms - lb.connect_timeout = connect_timeout_ms - lb.connect_retries = connect_retries + lb.timeout = timeout_ms + lb.retries = retries lb.devices = [device] # write to database @@ -491,20 +541,42 @@ class LoadBalancersController(RestController): address=None, id=None, type='ASSIGNING', ipVersion='IPV4' ) return_data.virtualIps = [vip_resp] + + nodes = session.query( + Node.id, Node.address, Node.port, Node.status, + Node.enabled, Node.weight + ).join(LoadBalancer.nodes).\ + filter(LoadBalancer.tenantid == tenant_id).\ + filter(LoadBalancer.id == lb.id).\ + all() + return_data.nodes = [] - for node in body.nodes: - if node.weight != Unset and node.weight != 1: - out_node = LBRespNode( - port=str(node.port), address=node.address, - condition=node.condition, weight=weight + for node in nodes: + if node.enabled == 1: + condition = 'ENABLED' + else: + condition = 'DISABLED' + + if node.weight == 1: + return_data.nodes.append( + LBRespNode( + id=str(node.id), port=str(node.port), + address=node.address, condition=condition, + status=node.status + ) ) else: - out_node = LBRespNode( - port=str(node.port), address=node.address, - condition=node.condition + return_data.nodes.append( + LBRespNode( + id=str(node.id), port=str(node.port), + address=node.address, condition=condition, + status=node.status, weight=str(node.weight) + ) ) - return_data.nodes.append(out_node) + return_data.options = LBOptions(timeout=timeout_ms, + retries=retries) + session.commit() # trigger gearman client to create new lb submit_job( @@ -551,6 +623,34 @@ class LoadBalancersController(RestController): if body.algorithm != Unset: lb.algorithm = body.algorithm + if body.options: + if body.options.timeout != Unset: + try: + timeout_ms = int(body.options.timeout) + if timeout_ms < 0 or timeout_ms > self.LB_TIMEOUT_MAX: + raise ClientSideError( + 'timeout must be between 0 and {0} ms' + .format(self.LB_TIMEOUT_MAX) + ) + lb.timeout = timeout_ms + except ValueError: + raise ClientSideError( + 'timeout must be an integer' + ) + if body.options.retries != Unset: + try: + retries = int(body.options.retries) + if retries < 0 or retries > self.LB_RETRIES_MAX: + raise ClientSideError( + 'retries must be between 0 and {0}' + .format(self.LB_RETRIES_MAX) + ) + lb.retries = retries + except ValueError: + raise ClientSideError( + 'retries must be an integer' + ) + lb.status = 'PENDING_UPDATE' device = session.query( Device.id, Device.name, Device.status diff --git a/libra/api/model/validators.py b/libra/api/model/validators.py index fdf1f87f..9052f8b7 100644 --- a/libra/api/model/validators.py +++ b/libra/api/model/validators.py @@ -27,10 +27,12 @@ class LBNode(Base): class LBRespNode(Base): + id = wtypes.text port = wtypes.text address = wtypes.text condition = wtypes.text - weight = int + status = wtypes.text + weight = wtypes.text class LBNodePut(Base): @@ -60,10 +62,8 @@ class LBVip(Base): class LBOptions(Base): - client_timeout = int - server_timeout = int - connect_timeout = int - connect_retries = int + timeout = int + retries = int class LBPost(Base): @@ -79,6 +79,7 @@ class LBPost(Base): class LBPut(Base): name = wtypes.text algorithm = Enum(wtypes.text, 'ROUND_ROBIN', 'LEAST_CONNECTIONS') + options = wsattr('LBOptions') class LBVipResp(Base): @@ -106,6 +107,7 @@ class LBResp(Base): updated = wtypes.text virtualIps = wsattr(['LBVipResp']) nodes = wsattr(['LBRespNode']) + options = wsattr('LBOptions') class LBMonitorPut(Base): diff --git a/libra/common/api/gearman_client.py b/libra/common/api/gearman_client.py index 33f64ce0..13c3cb8e 100644 --- a/libra/common/api/gearman_client.py +++ b/libra/common/api/gearman_client.py @@ -443,13 +443,12 @@ class GearmanClientThread(object): # All new LBs created since these options were supported # will have default values in the DB. Pre-existing LBs will # not have any values, so we need to check for that. - if any([lb.client_timeout, lb.server_timeout, - lb.connect_timeout, lb.connect_retries]): + if any([lb.timeout, lb.retries]): lb_data['options'] = { - 'client_timeout': lb.client_timeout, - 'server_timeout': lb.server_timeout, - 'connect_timeout': lb.connect_timeout, - 'connect_retries': lb.connect_retries + 'client_timeout': lb.timeout, + 'server_timeout': lb.timeout, + 'connect_timeout': lb.timeout, + 'connect_retries': lb.retries } lb_data['monitor'] = monitor_data diff --git a/libra/common/api/lbaas.py b/libra/common/api/lbaas.py index b603e875..d2df7860 100644 --- a/libra/common/api/lbaas.py +++ b/libra/common/api/lbaas.py @@ -111,10 +111,8 @@ class LoadBalancer(DeclarativeBase): tenantid = Column(u'tenantid', VARCHAR(length=128), nullable=False) updated = Column(u'updated', FormatedDateTime(), nullable=False) created = Column(u'created', FormatedDateTime(), nullable=False) - client_timeout = Column(u'client_timeout', INTEGER(), nullable=True) - server_timeout = Column(u'server_timeout', INTEGER(), nullable=True) - connect_timeout = Column(u'connect_timeout', INTEGER(), nullable=True) - connect_retries = Column(u'connect_retries', INTEGER(), nullable=True) + timeout = Column(u'timeout', INTEGER(), nullable=True) + retries = Column(u'retries', INTEGER(), nullable=True) nodes = relationship( 'Node', backref=backref('loadbalancers', order_by='Node.id') ) diff --git a/libra/common/api/lbaas.sql b/libra/common/api/lbaas.sql index 23c3803f..99dd63fc 100644 --- a/libra/common/api/lbaas.sql +++ b/libra/common/api/lbaas.sql @@ -26,10 +26,8 @@ CREATE TABLE loadbalancers ( created TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', # timestamp of when LB was created updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, # timestamp of when LB was last updated errmsg VARCHAR(128) DEFAULT NULL, # optional error message which can describe details regarding LBs state, can be blank if no error state exists - client_timeout INT, - server_timeout INT, - connect_timeout INT, - connect_retries INT, + timeout INT, + retries INT, PRIMARY KEY (id) # ids are unique accross all LBs ) DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci;