Merge "[API] Support new GALERA protocol in the API"

This commit is contained in:
Jenkins
2013-09-27 20:36:37 +00:00
committed by Gerrit Code Review
7 changed files with 88 additions and 8 deletions

View File

@@ -180,6 +180,16 @@ class LoadBalancersController(RestController):
raise ClientSideError(
'At least one backend node needs to be supplied'
)
# When the load balancer is used for Galera, we need to do some
# sanity checking of the nodes to make sure 1 and only 1 node is
# defined as the primary node.
if body.protocol and body.protocol.lower() == 'galera':
is_galera = True
else:
is_galera = False
num_galera_primary_nodes = 0
for node in body.nodes:
if node.address == Unset:
raise ClientSideError(
@@ -194,6 +204,7 @@ class LoadBalancersController(RestController):
'Node {0} port number {1} is invalid'
.format(node.address, node.port)
)
try:
node.address = ipfilter(node.address, conf.ip_filters)
except IPOutOfRange:
@@ -205,6 +216,19 @@ class LoadBalancersController(RestController):
raise ClientSideError(
'IP Address {0} not valid'.format(node.address)
)
is_backup = False
if node.backup != Unset and node.backup == 'TRUE':
is_backup = True
if is_galera and not is_backup:
num_galera_primary_nodes += 1
# Galera sanity checks
if is_galera and num_galera_primary_nodes != 1:
raise ClientSideError(
'Galera load balancer must have exactly one primary node'
)
with db_session() as session:
lblimit = session.query(Limits.value).\
filter(Limits.name == 'maxLoadBalancers').scalar()
@@ -241,8 +265,13 @@ class LoadBalancersController(RestController):
lb = LoadBalancer()
lb.tenantid = tenant_id
lb.name = body.name
if body.protocol and body.protocol.lower() == 'tcp':
lb.protocol = 'TCP'
if body.protocol:
if body.protocol.lower() in ('tcp', 'http', 'galera'):
lb.protocol = body.protocol.upper()
else:
raise ClientSideError(
'Invalid protocol %s' % body.protocol
)
else:
lb.protocol = 'HTTP'
@@ -255,8 +284,10 @@ class LoadBalancersController(RestController):
else:
if lb.protocol == 'HTTP':
lb.port = 80
else:
elif lb.protocol == 'TCP':
lb.port = 443
elif lb.protocol == 'GALERA':
lb.port = 3306
lb.status = 'BUILD'
lb.created = None
@@ -351,9 +382,16 @@ class LoadBalancersController(RestController):
else:
enabled = 1
node_status = 'ONLINE'
if node.backup == 'TRUE':
backup = 1
else:
backup = 0
out_node = Node(
lbid=lb.id, port=node.port, address=node.address,
enabled=enabled, status=node_status, weight=1
enabled=enabled, status=node_status, weight=1,
backup=backup
)
session.add(out_node)

View File

@@ -146,6 +146,7 @@ class NodesController(RestController):
raise ClientSideError(
'IP Address {0} not valid'.format(node.address)
)
with db_session() as session:
load_balancer = session.query(LoadBalancer).\
filter(LoadBalancer.tenantid == tenant_id).\
@@ -157,20 +158,36 @@ class NodesController(RestController):
raise NotFound('Load Balancer not found')
load_balancer.status = 'PENDING_UPDATE'
# check if we are over limit
nodelimit = session.query(Limits.value).\
filter(Limits.name == 'maxNodesPerLoadBalancer').scalar()
nodecount = session.query(Node).\
filter(Node.lbid == self.lbid).count()
if (nodecount + len(body.nodes)) > nodelimit:
session.rollback()
raise OverLimit(
'Command would exceed Load Balancer node limit'
)
return_data = LBNodeResp()
return_data.nodes = []
is_galera = False
if load_balancer.protocol.lower() == 'galera':
is_galera = True
for node in body.nodes:
is_backup = False
if node.backup != Unset and node.backup == 'TRUE':
is_backup = True
# Galera load balancer sanity checking. Only allowed to add
# backup nodes since a primary is presumably already defined.
if is_galera and not is_backup:
raise ClientSideError(
'Galera load balancer may have only one primary node'
)
if node.condition == 'DISABLED':
enabled = 0
node_status = 'OFFLINE'
@@ -179,7 +196,8 @@ class NodesController(RestController):
node_status = 'ONLINE'
new_node = Node(
lbid=self.lbid, port=node.port, address=node.address,
enabled=enabled, status=node_status, weight=1
enabled=enabled, status=node_status,
weight=1, backup=int(is_backup)
)
session.add(new_node)
session.flush()
@@ -194,6 +212,7 @@ class NodesController(RestController):
status=new_node.status
)
)
device = session.query(
Device.id, Device.name, Device.status
).join(LoadBalancer.devices).\
@@ -214,6 +233,7 @@ class NodesController(RestController):
@wsme_pecan.wsexpose(None, body=LBNodePut, status_code=202)
def put(self, body=None):
""" Update a node condition: ENABLED or DISABLED """
if not self.lbid:
raise ClientSideError('Load Balancer ID has not been supplied')
if not self.nodeid:
@@ -301,7 +321,9 @@ class NodesController(RestController):
if load_balancer is None:
session.rollback()
raise NotFound("Load Balancer not found")
load_balancer.status = 'PENDING_UPDATE'
nodecount = session.query(Node).\
filter(Node.lbid == self.lbid).\
filter(Node.enabled == 1).count()
@@ -311,6 +333,7 @@ class NodesController(RestController):
raise ClientSideError(
"Cannot delete the last enabled node in a load balancer"
)
node = session.query(Node).\
filter(Node.lbid == self.lbid).\
filter(Node.id == node_id).\
@@ -320,6 +343,14 @@ class NodesController(RestController):
raise NotFound(
"Node not found in supplied Load Balancer"
)
# May not delete the primary node of a Galera LB
if load_balancer.protocol.lower() == 'galera' and node.backup == 0:
session.rollback()
raise ClientSideError(
"Cannot delete the primary node in a Galera load balancer"
)
session.delete(node)
device = session.query(
Device.id, Device.name

View File

@@ -46,6 +46,10 @@ class Responses(object):
{
'name': 'TCP',
'port': '443'
},
{
'name': 'GALERA',
'port': '3306'
}
]
}

View File

@@ -22,6 +22,7 @@ class LBNode(Base):
port = wsattr(int, mandatory=True)
address = wsattr(wtypes.text, mandatory=True)
condition = Enum(wtypes.text, 'ENABLED', 'DISABLED')
backup = Enum(wtypes.text, 'TRUE', 'FALSE')
class LBRespNode(Base):

View File

@@ -272,11 +272,15 @@ class GearmanClientThread(object):
if not node.enabled:
continue
condition = 'ENABLED'
backup = 'FALSE'
if node.backup != 0:
backup = 'TRUE'
node_data = {
'id': node.id, 'port': node.port,
'address': node.address, 'weight': node.weight,
'condition': condition
'condition': condition, 'backup': backup
}
lb_data['nodes'].append(node_data)
# Track if we have a DEGRADED LB
if node.status == 'ERROR':

View File

@@ -117,7 +117,7 @@ class Node(DeclarativeBase):
__tablename__ = 'nodes'
#column definitions
address = Column(u'address', VARCHAR(length=128), nullable=False)
enabled = Column(u'enabled', Integer(), nullable=False)
enabled = Column(u'enabled', INTEGER(), nullable=False)
id = Column(u'id', BIGINT(), primary_key=True, nullable=False)
lbid = Column(
u'lbid', BIGINT(), ForeignKey('loadbalancers.id'), nullable=False
@@ -125,6 +125,7 @@ class Node(DeclarativeBase):
port = Column(u'port', INTEGER(), nullable=False)
status = Column(u'status', VARCHAR(length=128), nullable=False)
weight = Column(u'weight', INTEGER(), nullable=False)
backup = Column(u'backup', INTEGER(), nullable=False, default=0)
class HealthMonitor(DeclarativeBase):

View File

@@ -38,6 +38,7 @@ CREATE TABLE loadbalancers (
weight INT NOT NULL, # Node weight if applicable to algorithm used
enabled BOOLEAN NOT NULL, # is node enabled or not
status VARCHAR(128) NOT NULL, # status of node 'OFFLINE', 'ONLINE', 'ERROR', this value is reported by the device
backup BOOLEAN NOT NULL DEFAULT FALSE, # true if a backup node
PRIMARY KEY (id) # ids are unique accross all Nodes
) DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci;