python-libraclient/libraclient/v1_1/loadbalancer.py

313 lines
10 KiB
Python

# Copyright 2013 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 libraclient.openstack.common.apiclient.base import getid, Resource
from libraclient.v1_1.base import Manager
import socket
class Node(Resource):
def __repr__(self):
s = '<Node: {address}:{port}>'
return s.format(**self._info)
class Monitor(Resource):
def __repr__(self):
return '<Monitor: %s>' % self.name
class VirtualIP(Resource):
def __repr__(self):
s = '<VIP: {id} - {type} {ipVersion} {address}>'
return s.format(**self._info)
class LoadBalancer(Resource):
def __init__(self, manager, info, loaded=False):
info['nodes'] = [Node(self, n) for n in info.pop('nodes', [])]
info['virtualIps'] = [VirtualIP(self, n)
for n in info.pop('virtualIps', [])]
super(LoadBalancer, self).__init__(manager, info, loaded=False)
def __repr__(self):
return '<LoadBalancer: %s>' % self.name
def delete(self):
self.manager.delete(self)
def update(self, **kw):
self.manager.update(self, **kw)
def create_node(self, node):
return self.manager.create_node(node)
def list_nodes(self):
return self.manager.list_nodes(self)
def get_node(self, node):
return self.manager.get_node(self, node)
def update_node(self, node, condition=None, weight=None):
return self.manager.update_node(self, node,
condition=condition, weight=weight)
def delete_node(self, node):
return self.manager.delete_node(self, node)
def update_monitor(self, type_, delay, timeout,
attempts, path=None):
return self.manager.update_monitor(
self, type_=type_, delay=delay, timeout=timeout, attempts=attempts,
path=path)
def delete_monitor(self, lb):
self.manager.delete_monitor(lb)
def list_vip(self):
return self.manager.list_vip(self)
class LoadBalancerManager(Manager):
resource_class = LoadBalancer
def _parse_nodes(self, nodes):
out_nodes = []
try:
for node in nodes:
nodeopts = node.split(':')
ipaddr = nodeopts[0]
port = nodeopts[1]
weight, backup = None, None
# Test IP valid
# TODO: change to pton when we want to support IPv6
socket.inet_aton(ipaddr)
# Test port valid
if int(port) < 0 or int(port) > 65535:
raise Exception('Port out of range')
# Process the rest of the node options as key=value
for kv in nodeopts[2:]:
key, value = kv.split('=')
key = key.lower()
value = value.upper()
if key == 'weight':
weight = int(value)
elif key == 'backup':
backup = value # 'TRUE' or 'FALSE'
else:
raise Exception("Unknown node option '%s'" % key)
node_def = {'address': ipaddr, 'port': port}
if weight:
node_def['weight'] = weight
if backup:
node_def['backup'] = backup
out_nodes.append(node_def)
except Exception as e:
raise Exception("Invalid value specified for --node: %s" % e)
return out_nodes
def create(self, name, nodes, port=None, protocol=None, algorithm=None,
virtual_ip=None):
"""
Create a LoadBalancer from given values.
:param name: The name / display name.
:param nodes: Nodes.
:param port: Numeric port (80, 443 for example.)
:param protocol: Protocol to use (TCP / HTTP for example.)
:param algorithm: Algorithm (ROUND_ROBIN for example.)
:param virtual_ip: VIP ID to set if Shared LB.
"""
parsed_nodes = self._parse_nodes(nodes)
body = {
'name': name,
'nodes': parsed_nodes,
}
if port is not None:
body['port'] = port
if protocol is not None:
body['protocol'] = protocol
if algorithm is not None:
body['algorithm'] = algorithm
if virtual_ip is not None:
body['virtualIps'] = [{'id': virtual_ip}]
return self._post('/loadbalancers', body)
def get(self, lb):
"""
Get a LoadBalancer.
:param lb: The :class:`LoadBalancer` (or its ID) to update.
"""
lb = self._get('/loadbalancers/%s' % getid(lb))
return lb
def list(self, deleted=False):
"""
List loadBalancers.
:param deleted: Show deleted LoadBalancers.
"""
params = {}
if deleted:
params['status'] = 'DELETED'
url = self.build_url('/loadbalancers', params)
lbs = self._list(url, 'loadBalancers')
return lbs
def update(self, lb, name=None, algorithm=None):
"""
Update a LoadBalancer
:param lb: The :class:`LoadBalancer` (or its ID).
:param name: Set the name of the LoadBalancer.
:param algorithm: Algorithm (ROUND_ROBIN for example.)
"""
data = {}
if name is not None:
data['name'] = name
if algorithm is not None:
data['algorithm'] = algorithm
return self._put('/loadbalancers/%s' % getid(lb), data)
def delete(self, lb):
"""
Delete a LoadBalancer
:param lb: The :class:`LoadBalancer` (or its ID).
"""
self._delete('/loadbalancers/%s' % getid(lb))
def create_node(self, lb, node):
data = {}
data['nodes'] = self._parse_nodes(node)
url = '/loadbalancers/%s/nodes' % getid(lb)
nodes = self._post(url, data, return_raw=True, response_key='nodes')
return [Node(self, n) for n in nodes]
def get_node(self, lb, node):
"""
Get a Node belonging to a LoadBalancer
:param lb: The :class:`LoadBalancer` (or its ID).
:param node: The :class:`Node` (or its ID).
"""
url = '/loadbalancers/%s/nodes/%s' % (getid(lb), getid(node))
return self._get(url, obj_class=Node)
def list_nodes(self, lb):
"""
List Nodes belonging to a LoadBalancer.
:param lb: The :class:`LoadBalancer` (or its ID)..
"""
url = '/loadbalancers/%s/nodes' % getid(lb)
return self._list(url, 'nodes', obj_class=Node)
def update_node(self, lb, node, condition=None, weight=None):
"""
Update a node
:param lb: The :class:`LoadBalancer` (or its ID).
:param node: The :class:`Node` (or its ID).
:param condition: Set the conditioon.
:param weight: Set the weight.
"""
data = {}
if condition is not None:
data['condition'] = condition
if weight is not None:
data['weight'] = weight
url = '/loadbalancers/%s/nodes/%s' % (getid(lb), getid(node))
return self._put(url, data, obj_class=Node)
def delete_node(self, lb, node):
"""
Delete a node from a LoadBalancer.
:param lb: The :class:`LoadBalancer` (or its ID).
:param node: The :class:`Node` (or its ID).
"""
url = '/loadbalancers/%s/nodes/%s' % (getid(lb), getid(node))
self._delete(url)
def get_monitor(self, lb):
"""
Get a Monitor for a LoadBalancer
:param lb: The :class:`LoadBalancer` (or its ID).
"""
url = '/loadbalancers/%s/healthmonitor' % getid(lb)
return self._get(url, obj_class=Monitor)
def update_monitor(self, lb, type_, delay, timeout,
attempts, path=None):
"""
Update a Monitor in a LoadBalancer
:param lb: The :class:`LoadBalancer` (or its ID).
:param type_: Monitor type.
:param delay: Numeric delay, must be less then timeout.
:param timeout: Numeric timeout, must be geater then delay.
:param attempts: Max attempts before deactivation.
:param path: URI path when using HTTP type.
"""
data = {}
data['type'] = type_
if timeout > delay:
raise ValueError('Timeout can\'t be greater than Delay')
data['delay'] = delay
data['timeout'] = timeout
data['attemptsBeforeDeactivation'] = attempts
if type_.upper() != 'CONNECT':
data['path'] = path
url = '/loadbalancers/%s/healthmonitor' % getid(lb)
return self._put(url, data, obj_class=Monitor)
def delete_monitor(self, lb):
"""
Delete monitor from a LoadBalancer
:param lb: The :class:`LoadBalancer` (or its ID).
"""
url = '/loadbalancers/%s/healthmonitor' % getid(lb)
self._delete(url)
def list_vip(self, lb):
"""
List Virtual IPs for a LoadBalancer
:param lb: The :class:`LoadBalancer` (or its ID) to update.
"""
return self._list('loadbalancers/%s/virtualips' % getid(lb),
response_key='virtualIps', obj_class=VirtualIP)
def send_logs(self, lb, values):
"""
Send a snapshot of logs somewhere.
:param lb: The :class:`LoadBalancer` (or its ID).
:param storage: Storage type.
:param kw: The values to send it with, pass as kw.
"""
self.client.post('/loadbalancers/%s/logs' % getid(lb), json=values)